Hi there, and welcome to another article on web development. This week we’ll be looking at Knockout.js, an MVVM library that makes data binding in HTML and JavaScript a whole lot easier. If that meant nothing to you don’t worry, that’s what I’m here for. If you’re unfamiliar with HTML and/or JavaScript I suggest you follow my series on starting web development. You can find the first part here: Web development #1: Internet and the World Wide Web.
If you like my blogs please subscribe. You can enter your email at the top right corner of this page. Don’t worry, I’m not going to sell you anything, but you’ll get new posts in your mailbox as soon as I post them.
Also, if you haven’t done so yet, make sure to follow me on Twitter @sanderrossel for other interesting articles and industry updates.
All the examples for this blog are published on my GitHub account. You can get them at the knockout-blog repo.
So with that out of the way, let’s get started!
Knockout.js basics
So what exactly is this Knockout.js? Simply said Knockout links data, from your JavaScript, to your HTML page and updates that data if it changes, either from your page (by user action) or in your JavaScript. This linking is called binding.
As mentioned Knockout is an MVVM library, which stands for Model View ViewModel, but that still doesn’t tell us much. Well, look at it this way, you probably have data to show on your page, this is your Model. This can be anything, like user information, blog posts or a list of sales orders. Your HTML page is your View, it’s what people see. The ViewModel is an abstract intermediary that communicates between your View and your Model. The key for this communication is a binder, a component that takes care of this communication.
So that sounds pretty abstract and difficult, right? Here’s the good news though, you can pretty much forget about it. The takeaway is that MVVM enables for a separation of your View (user interface, or (G)UI) and your domain data and logic. That also means you can, theoretically, rewrite your entire View without having to touch your domain. And it means one person can be writing the View while another is working on the business logic, boosting a team’s productivity. The Windows Presentation Foundation (WPF) is another example of MVVM.
So Knockout is an MVVM library, but what does this mean for us developers? How about an example? Of course we’ll need an HTML page and some JavaScript. That’s all, so I’m not going to incorporate a back end. I did, however, make use of jQuery, to show the real strength of Knockout. So I’m going to show two input fields where you can enter your first and your last name. Whenever you change either your first or last name your full name is shown below the inputs. Easy as pie. First you’ll need to get Knockout and jQuery. Head over to the Knockout download page (version 3.3.0) and the jQuery download page (version 2.1.3) to get them. Of course if you got the source from my GitHub you’re already good to go. So here’s the HTML.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Knockout example</title>
</head>
<body>
<div>
<h2>jQuery form</h2>
<p>First name: <input id="firstName" /></p>
<p>Last name: <input id="lastName" /></p>
<h2>Hello, <span id="fullName"></span>!</h2>
</div>
<div>
<h2>Knockout form</h2>
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<h2>Hello, <span data-bind="text: fullName"></span>!</h2>
</div>
<script src="jquery-2.1.3.min.js"></script>
<script src="knockout-3.3.0.js"></script>
<script src="HelloWorld.js"></script>
</body>
</html>
And here’s the HelloWorld.js.
$(function () {
$('#firstName').change(function () {
var fullName = $(this).val() + ' ' + $('#lastName').val();
$('#fullName').text(fullName);
})
.val('Sander');
$('#lastName').change(function () {
var fullName = $('#firstName').val() + ' ' + $(this).val();
$('#fullName').text(fullName);
})
.val('Rossel')
.change();
var person = {
firstName: ko.observable(''),
lastName: ko.observable('')
};
person.firstName('Sander');
person.lastName('Rossel');
person.fullName = ko.pureComputed(function() {
return this.firstName() + " " + this.lastName();
}, person);
ko.applyBindings(person);
});
So that’s a lot of code! Let’s break it down. The HTML for the jQuery form is simple and straightforward, so let’s skip that. The HTML for the Knockout form has some weird stuff though, what’s all this data-bind? This is a custom Knockout attribute (all custom HTML5 attributes have the data- prefix) that defines what attributes on the HTML element should bind to what properties on your binding context (we’ll see that in a minute). The binding then makes sure that any changes in your form are reflected in your data and vice versa. So, for example, the input that takes your first name has its value attribute bound to some firstName property on the binding context. The syntax for the binding is “name: value [, anotherName: someValue]”. We’ll see a couple of different bindings throughout this post.
Now if we look at the JavaScript we first see the jQuery implementation. This makes us select the #firstName and #lastName elements, get their value and then update the #fullName element. It’s just all DOM traversal really, nothing special about it. Now the Knockout implementation is something different! We define an object (which is really our ViewModel) with a firstName and a lastName and we assign to them whatever ko.observable(initialValue) returns. And here is the trick, really. These observables notify our page when a change is made.
After that I’m setting the values for firstName and lastName. I could’ve done that right away, but I want to emphasize that firstName and lastName are not simply our first and last names (strings), but they are actually functions (because that’s what ko.observable() returned). Changing them will require us to invoke that function and passing in the new value as a parameter. Likewise, getting the values requires us to invoke these functions, but passing in no parameters.
After that I’m giving person its fullName property, assigning to it the return value of ko.pureComputed(function, this). I didn’t specify it in the object literal, because ko.pureComputed() takes a parameter that serves as this. In the object literal I can’t use person just yet, because we’re creating it and the this will be the window if I’m not specifying it. The computed value keeps track of all its bindings (firstName and lastName) and updates automatically when one of them changes.
Last, but not least I’m calling ko.applyBindings(context) and pass it our person object. This sets the binding context and will cause our page to be updated and show the values of our person object.
I don’t know about you, but I’m not so excited about that pureComputed… So let me give you a little alternative to creating that person using a function and a constructor.
var Person = function(firstName, lastName) {
this.firstName = ko.observable(firstName);
this.lastName = ko.observable(lastName);
this.fullName = ko.pureComputed(function() {
return this.firstName() + ' ' + this.lastName();
}, this);
}
var person = new Person('Sander', 'Rossel');
Better, no?
So that’s a whole lot to take in and with a few lines of jQuery you can get the exact same result! So why then would we use Knockout?
Building a contact form
Well, first of all, with jQuery you need to manually update variables in memory and values on your page. With just a name this isn’t a big deal, but what if you had an entire contact form? Let’s create one using Knockout. You should see that our JavaScript code isn’t growing much bigger, while this would be quite a hassle using jQuery. So here’s some contact form HTML.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Knockout example</title>
</head>
<body>
<div>
<h1>Contact</h1>
<p>First name: <input data-bind="value: firstName" /></p>
<p>Last name: <input data-bind="value: lastName" /></p>
<p>Email: <input type="email" data-bind="value: email" /></p>
<p>Subject: <input data-bind="value: subject" /></p>
<p>Your question: <textarea rows="5" data-bind="value: content"></textarea></p>
<p><button data-bind="click: submit">Submit</button></p>
</div>
<script src="knockout-3.3.0.js"></script>
<script src="ContactForm.js"></script>
</body>
</html>
And here’s the ContactForm.js.
var form = {
firstName: ko.observable(''),
lastName: ko.observable(''),
email: ko.observable(''),
subject: ko.observable(''),
content: ko.observable(''),
submit: function() {
alert("You entered:"
+ "\nFirstname: " + this.firstName()
+ "\nLastname: " + this.lastName()
+ "\nEmail: " + this.email()
+ "\nSubject: " + this.subject()
+ "\nContent:\n" + this.content());
}
};
ko.applyBindings(form);
Now that’s looking more like it! We simply create some object, bind it to our form and we have nothing else to worry about. Notice that I bind the click event of the button to the submit function, this is called the click binding.
Now here you should see some real gain from Knockout! Look at that code, it’s just an object and when submit is called we have the most recent values. No need for any DOM traversal on our side. The beauty is that you can use this form obect anywhere in your code and nowhere do you have to worry about getting the correct values from- or updating the correct values to the DOM.
Arrays and foreach
So what if we have an array of items and we want to bind to that? Luckily Knockout has an observableArray which makes this pretty easy. So let’s say we have an array with Album objects. Each album has an artist, name and genre. If we’d create a table it may look like this.
<table>
<thead>
<tr>
<th>Artist</th>
<th>Name</th>
<th>Genre</th>
</tr>
<thead>
<tbody data-bind="foreach: albums">
<tr>
<td data-bind="text: artist"></td>
<td data-bind="text: name"></td>
<td data-bind="text: genre"></td>
</tr>
</tbody>
</table>
So albums is just an observableArray, which is created in much the same way as a regular observable. We can simply bind an observableArray using the foreach binding. The artist, name and genre in the data-binds are called on the current object in the foreach.
So let’s say we want to provide some edit, add and remove functionality so we can manipulate our list and albums. Actually that’s not much harder!
<table>
<thead>
<tr>
<th>Artist</th>
<th>Name</th>
<th>Genre</th>
<th></th>
</tr>
<thead>
<tbody data-bind="foreach: albums">
<tr>
<td><input data-bind="value: artist" /></td>
<td><input data-bind="value: name" /></td>
<td><input data-bind="value: genre" /></td>
<td><button data-bind="click: $parent.remove">X</button>
</tr>
</tbody>
</table>
<button data-bind="click: add">Add</button>
So the click of the button in our grid is calling the remove function on our $parent object, where $parent is the container of our array. There are other such variables, such as $data (the current object) and $index (the current index).
Notice that the add button simply calls the add function, which is on our current binding context outside of the foreach loop.
Let’s look at the JavaScript for both examples.
var Album = function(artist, name, genre) {
this.artist = ko.observable(artist);
this.name = ko.observable(name);
this.genre = ko.observable(genre);
}
var ViewModel = function() {
var self = this;
self.albums = ko.observableArray([
new Album('The Beatles', "Sgt. Pepper's Lonely Hearts Club Band", 'Rock'),
new Album('Led Zeppelin', 'Led Zeppelin III', 'Rock'),
new Album('The Prodigy', 'The Fat Of The Land', 'Dance')
]);
self.add = function() {
self.albums.push(new Album());
};
self.remove = function() {
self.albums.remove(this);
};
};
ko.applyBindings(new ViewModel());
So first I’m simply defining the Album object. After that I’m creating a container for our array and our add and remove functionality. The first thing you’ll notice is that I’m making a reference to this by storing it in self. That’s a little weird JavaScript thing, which is nicely explained in this article: Getting Out of Binding Situations in JavaScript. Basically the remove function will not be called in the context of your ViewModel.
So after that we’re creating an observableArray (notice how it’s much the same as a regular observable). And then we have an add and remove function. We can then create a new instance and apply the bindings to our context.
You will notice that if you put both tables in your HTML (like I did) that the read-only table gets updated when you update the editable table. How awesome is that? And in your JavaScript all albums are, of course, automatically synced.
So one more thing, let’s say you can’t use a foreach binding because you don’t have a container, like a table or list. Knockout has a solution for this scenario too.
<ul>
<li><h3>My favourite albums list!</h3></li>
<!--
<li><span data-bind="text: name"></li>
<!--
</ul>
And even that one automatically syncs when an album is changed, added or removed.
Other bindings
So you’ve now seen some of the most important features of Knockout, but there’s still much more. For example, there’s the if binding, which looks a lot like the foreach binding.
<p><input type="checkbox" data-bind="checked: showText" />(Un)check me</p>
<p data-bind="if: showText">This is the if binding.</p>
<p data-bind="visible: showText">This is the visible binding.</p>
<!--
<p>This is the commented if.</p>
<!--
var ViewModel = function() {
this.showText = ko.observable(true);
}
ko.applyBindings(new ViewModel());
Next to the if binding there is a visible binding in there. Notice how the three act the same, but are actually different. You can check that when you press F12 in your browser and check the page’s HTML when you check and uncheck the checkbox. While the visible binding simply sets the style of the paragraph to display: none, the if binding removes the element from the DOM.
The attr binding let’s you set any attribute of an element. Here are some examples.
<p><input type="email" data-bind="attr: { placeholder: emailPlaceholder }" /></p>
<p><a data-bind="attr: { href: url, title: urlTitle, target: urlTarget }">This is a link.</a></p>
var ViewModel = function() {
this.emailPlaceholder = ko.observable('example@contoso.com');
this.url = ko.observable('http://sanderrossel.com');
this.urlTitle = ko.observable("Sander's bits");
this.urlTarget = ko.observable('__blank');
}
ko.applyBindings(new ViewModel());
The attr binding has a little weird syntax. Basically you specify attr: and then a literal JavaScript object with the attributes as properties and the fields you want to bind to as values.
In this example, since none of the values are going to change anyway, you could opt for not making the values observables, which is perfectly valid and which will show your data correct (but, of course, will not update when the values are going to change).
How about dynamically adding and removing classes to your elements? We can do this using the css binding. It looks a lot like the attr binding, except we now specify the class we want to add and the condition when it should be added (of course you should add some CSS too, if you’re using my files from GitHub you’ll see a little embedded CSS in the header).
<p data-bind="css: { cool: temperature() <= 7, moderate: temperature() > 7 && temperature() < 20, hot: temperature() >= 20 }">Temperature: <input data-bind="value: temperature" /></p>
var ViewModel = function() {
this.temperature = ko.observable(15);
}
ko.applyBindings(new ViewModel());
You can also set your style directly using the style binding. I’ll leave that for you to figure out on your own.
Templates
So there’s one last very powerful feature I want to discuss, templates. So let’s go back to our album example. We now displayed them in a table, but what if we wanted to create regular inputs, perhaps create a little card view, instead of rows. You could use the comment style foreach, but what if you wanted this on other pages as well? You’d have to copy-paste your HTML onto the other page. Knockout has a better solution, the template binding.
So the idea is that you create some HTML which isn’t part of any page. We’re going to inject this HTML into our page, once for each album. And we can inject it on other pages too, if we want. It’s basically HTML re-use. So we can use the same JavaScript that we used in the arrays example. Here’s the HTML.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Knockout example</title>
</head>
<body>
<div>
<h1>My favourite albums</h1>
<div data-bind="foreach: albums">
<div data-bind="template: { name: 'album-template', data: $data}"></div>
</div>
</div>
<!--
<script type="text/html" id="album-template">
<h2 data-bind="text: name"></h2>
<p>Artist: <span data-bind="text: artist"></span><br>
Genre: <span data-bind="text: genre"><span></p>
</script>
<script src="knockout-3.3.0.js"></script>
<script src="ArraysAndForeach.js"></script>
</body>
</html><!DOCTYPE html>
So we’re looping through our albums using the foreach binding. Then for each album we inject the template, which gets the current album as its binding context ($data). As you’ll see the template is now inserted in our page once for every album. Pretty awesome!
Well, that’s it! I hope I could make Knockout.js a little easier for you. I won’t say it’s easy, because I’ve had my share of problems with it too. Luckily it’s all worth it in the end, since Knockout can make your life a lot easier and your code a lot more concise.
I should probably mention that Knockout wasn’t created as a replacement for jQuery, or any JavaScript library for that matter, and you can use Knockout with other libraries if you like.
Knockout has more to offer, I can especially recommend that you take a look at the Mapping plugin, that can create observable objects from regular objects. Other than that the Knockout website has a pretty nice tutorial and the documentation is pretty good too, so be sure to check them out.
Happy coding!
The post Knockout.js for data binding in HTML and JavaScript appeared first on Sander's bits.