Rapid and responsive UI development with Knockout.js
Knockout aims to simplify interactions and makes responsive interfaces as simple as updating a variable. In this tutorial, Umbraco developer Matt Brailsford takes us through creating a sample application
I’m sure all JavaScript developers have had that “oh…my…god!” moment when they were introduced to frameworks such as jQuery or MooTools for the first time. The realisation that what was a nightmare of cross browser wiring, and verbose commands has been replaced with a simple, succinct and universal interface making accessing the browser DOM a breeze.
Well, how those frameworks revolutionised DOM access, Knockout aims to revolutionise user interactions. By providing a simple two way binding mechanism between your UI and your view model, Knockout takes care of all the wiring and event binding to keep the two constantly in sync.
What follows is a two-part guide to creating a sample application, Slidr (a simple slideshow generator), that demonstrates some of the core features that most developers would use in real life. Starting with the fundamentals of Knockout and leading on to integrating Knockout with existing third party UI components. By the end you’ll be sure to be saying “oh … my … god!” once again.
Changes
Here at Umbraco, we are going through a lot of changes. Exciting changes. For a number of years now, we have been developing one of the strongest and most flexible open source CMS’s to run on the .NET framework (you may have seen the recent tutorial we ran in the February/March issues of .net magazine). During those years, the system has grown and adapted along with the needs of the community. Today we are embarking on one of the biggest enhancements to the system to date, Umbraco 5. A rebuilt and re-architected version using some of the latest advancements in web technology, ensuring that our system continues to grow and adapt for many more years to come.
As part of that upgrade there will be updates to how we handle the interactions of our users with the system. Today’s users expect more from a UI. They expect it to be slick and they expect it to be instant (thanks Apple). So after trailing the various frameworks out there, Knockout stood out to be our clear winner.
So why Knockout?
- It’s lightweight. At just 39kb or 13kb when using http compression, it’s light enough to use on any project.
- It works with any framework. Yeah, we use jQuery too, but it’s good to know it can work with anything.
- It’s well documented. So far there hasn’t been a question we’ve had that we couldn’t find the answer for in the documentation.
- It requires minimal changes to how you already work. There is nothing worse than a framework that asks you to forget everything you’ve learnt to date. With Knockout it sits on top of your existing knowledge.
So hopefully round about now you're feeling well up for taking Knockout for a spin, but rather than me running you through everything we’ve used it for in Umbraco 5 (that might be a rather long tutorial), I’ve put together a sample application to demonstrate some of the key features of Knockout that you are likely to use again and again (if you would like to see how we’ve used it in Umbraco 5 though, by all means go and download it for yourself over on CodePlex).
Get the Creative Bloq Newsletter
Daily design news, reviews, how-tos and more, as picked by the editors.
So without any further ado, I give you slidr, a Knockout slideshow generator.
To use slidr, simply enter a term to search for images on Flickr with and slidr will automatically look them up for you. From the results drag the images you like into the timeline along the bottom. You can sort the timeline by dragging images into the order you want them to appear, and change the title of an image by clicking it and editing it in the preview area. To remove an image from the timeline, you can hit the little cross icon in the corner of the image, and to launch your slideshow, just hit the launch button bottom right.
So if you haven’t done already, go give it a whirl. I’ll be right here when you get back.
Done it? Ok, let's get started then.
Creating the HTML
I assume you know a bit about HTML, so the idea here is to just create the design and layout for the app. Go nuts and design your own, or just use the HTML template available in the download. The main components you are going to need are: a search area, including a search input field, and a place to display the search results; a timeline area to add / arrange your selected photos; a preview area to show a larger preview of your selected photo, with a text field for editing the photos title; and finally a launch button to launch the slideshow.
Adding the scripts
Now that we have the design setup, it’s time to make it do some stuff. But before we can start scripting though, we need to include a few libraries. So go ahead and include the following scripts in the head section of your HTML document.
<script type="text/javascript" src="js/jquery-1.7.1.min.js"></script><script type="text/javascript" src="js/knockout-2.0.0.js"></script><script type="text/javascript" src="js/knockout.mapping-latest.js"></script>
We start by including the jQuery library, followed by the Knockout library and the Knockout mapping plug-in (we’ll get round to what this does shortly).
To keep our code separate from our presentation, we’ll also create and include a JavaScript file to hold all the code we are going to write in this tutorial. So in your scripts folder, just created a blank JavaScript file and include it in the head section as well.
<script type="text/javascript" src="js/slidr.js"></script>
Now that we have our includes registered, the fun can really begin.
Searching
The first thing we will implement is the image search. For this we are going to use Flickr and their JSON API. To use the Flickr API, you’ll need to sign up for an API key, or if you just want to try it out for this tutorial, you are welcome to just use my demo one: 60aa42175d788be84e5c4cb0d659e7ef.
Open up the script file you made in the last step and let's get rolling.
To start with, and one of the corner stones of Knockout, we’ll first create our view model. A view model is simply an object on which we define properties for the various variables we would like to keep track of:
var viewModel = { searchTerm: ko.observable(“”), searchTimeout: null, foundPhotos: ko.observableArray([])};
On our view model we have defined properties to hold the value of the entered search term, one to hold a timeout function that will perform our actual search on a slight delay and a property to hold the search results we get back from the search.
By now you’ve probably noticed the funny values of some of the properties we’ve just defined and are wondering what they are all about. Well, this is how you tell Knockout that you want your property to be bindable. By using ko.observable or ko.observableArray (for array type variables of course) Knockout will keep track of any changes to those properties, and notify any bound elements of those changes as soon as they occur. The parameters we pass in to the observable methods are simply the default values we want those properties to have.
Okay, so having observable properties is pretty pointless on their own, so next we will want to hook up the elements that will affect or be affected by changes to these properties. For this, we use a simple data binding syntax on our HTML elements.
On your search input field, add the following data-bind attribute:
<input id="search-term" type="text" data-bind="value: searchTerm, valueUpdate: 'afterkeydown', event: { keyup: search }" />
The data-bind attribute is what Knockout uses to bind an element to a view model property. It tells Knockout how it should affect that property, or how it should be affected by changes to that property.
So what is our search input field telling Knockout? Well, the first binding we have is value: searchTerm. What this is saying is that when changes are made to the inputs value attribute, it should automatically update the searchTerm property on our viewmodel. Additionally, if the searchTerm property is updated elsewhere in the app, then it should automatically be updated with the new value. The second binding we have is valueUpdate: ‘afterkeydown’. What this is telling Knockout is that it should update the connected property after a key down event is captured. By default, Knockout updates properties when a change event is captured (for input fields, this is usually when the field loses focus) but as we want to search for images as the user is typing, we can tell Knockout to update after key down instead.
Our last binding statement is event: { keyup: search }. Now this one is kind of interesting, and is a little bit different to the others, as this is basically telling Knockout to bind to an event raised by our input field, in this case keyup, and instead of updating a value, call a method on our view model, “search”. We don’t yet have a method “search” on our view model, so let’s go ahead and add one.
var viewModel = { searchTerm: ko.observable(“”), searchTimeout: null, foundPhotos: ko.observableArray([]), search: function(){ console.log(this.searchTerm()); }};
That’s right, on a view model you can define both properties and methods. Both of which can be bound to elements, and be automatically synced / triggered by Knockout when a change occurs.
Okay, so we’ve defined our view model, and added our bindings, now there is just one last thing we need to do to get Knockout to glue everything together.
$(function(){ ko.applyBindings(viewModel);});
What this does is kickstarting Knockout wiring up all the data-bind attributes we’ve defined in our HTML document with the relevant properties on the passed in view model (I’ve also wrapped that in a jQuery document.ready wrapper so that it only happens when the document is ready).
So now, we should be able to test our search input field to see if everything is working correctly. Open up your application in a browser, and using the developer toolbar, check the JavaScript console log. As you type in the search input field, you should start to see a log of the keys you entered after each key press.
Hazzaaa!
Now that we know our bindings work, let's flesh out the search method to perform an actual search:
var flickerApiKey = “YOUR_FLICKER_API_KEY”;var viewModel = { … search: function(){ // Empty the current found photos list viewModel.foundPhotos([]); // Only search if serch term is equal to or longer that 3 characters if(this.searchTerm().length >= 3){ // Clear any previously started searches clearTimeout(viewModel.searchTimeout); // Set a timeout to search in 1 second (Prevents seaching after every keypress) this.searchTimeout = setTimeout(function(){ // Peform the search var url = "http://api.flickr.com/services/rest/?method=flickr.photos.search&api_key="+ flickrApiKey +"&tags="+ viewModel.searchTerm() +"&per_page=100&format=json&jsoncallback=?"; $.getJSON(url, function(data){ if(data.stat == "ok") { // Map the results ko.mapping.fromJS(data.photos.photo, photoMappingOptions, viewModel.foundPhotos); } }); }, 1000); } }};
Hopefully most of this should make sense. Essentially a call to search clears out any previous results, then, if the search term is longer than three characters, triggers a new search. We delay the search by one second so that we don’t waste a call to the API while the user is typing. Finally, we call the Flickr API via a getJSON call and in the callback method, we map the results to an observable object.
The only things really worth mentioning here are, firstly, the way we are setting variables which are defined as observable, and secondly, the action of mapping a JSON object to an observable object.
When getting or setting an observable property, we must access them as if they are methods, so instead of viewModel.myProperty = “value”; we need to set a value like viewModel.myProperty(“value”); likewise, to get a value we must call viewModel.myProperty(); rather than just viewModel.myProperty;.
To help out when working with external data, Knockout come with some utility methods to help map the data to be observable. So rather than defining an object, and settings all the properties as observable yourself, Knockout can iterate over the properties of your data and covert them all to observable for you. To do this we use the ko.mapping.fromJS method. This method takes three arguments, the object to convert into an observable object, a settings object and the view model property to update with processed object.
By default, Knockout will just map all the properties it can to observables, however, we can modify or extend this using a settings object. In our example, we pass in a photoMappingOptions object. The code for this is as follows (you should add this to your script file too).
var photoMappingOptions = { 'create': function(o){ var photo = ko.mapping.fromJS(o.data); photo.smallImageUrl = "http://farm"+ photo.farm() +".static.flickr.com/"+ photo.server() +"/"+ photo.id() +"_"+ photo.secret() +"_s.jpg"; photo.mediumImageUrl = "http://farm"+ photo.farm() +".static.flickr.com/"+ photo.server() +"/"+ photo.id() +"_"+ photo.secret() +".jpg"; photo.largeImageUrl = "http://farm"+ photo.farm() +".static.flickr.com/"+ photo.server() +"/"+ photo.id() +"_"+ photo.secret() +"_z.jpg"; return photo; }}
What this is doing is extending the creation process of the mapper as it creates a new mapped object. First we run the default mapper to convert the object into an observable object and store a reference of the result. We then extend the observable object with a few helper properties of URLs to the various sizes of the photo. Lastly we return the final converted object.
So we should now have a search method that performs a search and stores the results in the viewModel.foundPhotos property. Now let's render those out to the screen. Add the following HTML snippet into the area you created for the search results.
<ul data-bind="foreach: foundPhotos"> <li data-bind="attr: { 'data-id' : id }"> <img data-bind="attr: { 'src' : smallImageUrl, 'alt' : title, title: title }" /> </li></ul>
Here we are using two new data-bind attributes, foreach and attr. The foreach attribute allows us to loop through a collection of objects, in our case the foundPhotos collection, and duplicate the inner HTML code block for each object (effectively the inner HTML code block becomes a template to use for each item in the collection). Each duplicated code block is then bound to the item at that index in the collection. Inside these templates, we then use the attr data-bind attribute to set various attributes on the HTML elements, such as the src, alt and title attributes of the image, and a helper data-id attribute on the list item to store a reference to the photos unique id.
If we try our application now, we should be able to search for a term and have our results display on the screen.
Conclusion
In this part we’ve looked at the fundamentals of Knockout, how to define a view model and bind it to HTML elements, as well as how to work with external data.
In part 2 we’ll look at how to use third party components together with Knockout and finish off our slidr app.
Additional resources
The aim of this tutorial is to show you some of the main concepts of Knockout, however I couldn’t possibly cover every little bit, so, if you’d like to learn more, be sure to check out the documentation section on the Knockout site.
If you found this tutorial a little too much, you can also try out the tutorials on the Knockout site, too. They’ll build your knowledge up gradually.
Thank you for reading 5 articles this month* Join now for unlimited access
Enjoy your first month for just £1 / $1 / €1
*Read 5 free articles per month without a subscription
Join now for unlimited access
Try first month for just £1 / $1 / €1
The Creative Bloq team is made up of a group of design fans, and has changed and evolved since Creative Bloq began back in 2012. The current website team consists of eight full-time members of staff: Editor Georgia Coggan, Deputy Editor Rosie Hilder, Ecommerce Editor Beren Neale, Senior News Editor Daniel Piper, Editor, Digital Art and 3D Ian Dean, Tech Reviews Editor Erlingur Einarsson and Ecommerce Writer Beth Nicholls and Staff Writer Natalie Fear, as well as a roster of freelancers from around the world. The 3D World and ImagineFX magazine teams also pitch in, ensuring that content from 3D World and ImagineFX is represented on Creative Bloq.