In the spirit of trying out new and interesting Javascript frameworks, and just new technologies in general, this week I take Knockout.js for a spin and it is not bad. Knockout.JS follows an MVVM pattern (Model-View-ViewModel), unlike other Javascript frameworks which follow the more traditional MVC pattern.
Knockout relies on observables and a declarative binding syntax on your front-end. Which essentially means that a variable or property can notify a DOM element when it has changed state, which, if you've worked on front-end is pretty useful. Here is a quick breakdown of the parts that make up the MVVM.
The model is your data, wherever it may come from.
The view is your front-end HTML and knockout.js binding directives.
And your viewmodel is your javascript that will handle your display and data logic.
If you're familiar with Angular.JS, it's somewhat similar but without Controllers or Modules. If you're coding in Visual Studio you get the added benefit that it supports full IntelliSense for Knockout.js and it comes set up in new projects by default.
Knockout.js is perfect for when you have a responsive website that needs to update itself based on user actions and such. If a user adds a new element to a list let's say, we'd want the list display to automatically show the new item. With knockout.js we can inform that list variable to keep an eye out for changes, and it will update the corresponding view without us having to do anything. If we were to do that ourselves in JavaScript, we'd quickly end up writing tons and tons of boilerplate for basic scenarios. So let's dive into a quick example and a few definitions of important key concepts.
Our Example, Let's Begin!
Because really that's all we're here for. So for this example I'll try to cover as many basic Knockout elements as I can without it getting too confusing. My data/model will be an object array filled with 'movie' objects, and I'll be displaying them onto my View, or web page, using a Knockout binding syntax that we'll get into down below. I'll show the syncing capabilities by adding new movies and by editing the current ones and showing them update in parallel in the View. I won't show deletions, but it's basically the same concept. Any additions or deletions on your array will be reflected on your View automatically. So let's dive in.
Create A Model
Your model is your data. It can be anything from a few variables in your javascript to perhaps JSON data being returned from a web service call. Since this is a quick guide we'll assume that our model in this case is a set of javascript variables that we'll use throughout the example. I'll hardcode the model data, but be sure that on most professional websites, the data will be coming from a database or from a web service, as well as from user input.
// we can declare this outside of our ViewModel
function movie(name, year)
{
var self = this;
self.name = name;
self.year = year;
}
Our model is a javascript object array with awesome movies. Note that we can declare this class outside of our ViewModel. Nothing special about it just yet, it's just a javascript class.
Create a ViewModel
Your ViewModel is your javascript code that will handle your data and your UI logic and operations. It can be a javascript file that you create that instantiates your Knockout.js framework and then adds Observable properties (more on that below) or it can be javascript directly thrown into your web page in a script tag. You define any and all functions here. Pay attention to the comments, as I'll kind of fly by the example.
Definition: Observables - observables - these are properties that automatically will issue notifications whenever their value changes.
<script type="text/javascript">
function movie(name, year)
{
var self = this;
// this property is Observable, which means any changes to it
// will notify the View
self.name = ko.observable(name);
self.year = year;
}
This is our same class from above, but we've made the 'name' property an Observable one, which means that it will notify any element it's bound to of changes to it.
// defines our view model
var MoviesViewModel = function () {
var self = this;
// let's declare 3 movies (can come from anywhere really)
var movie1 = new movie("The Matrix", "2000");
var movie2 = new movie("The Matrix Reloaded", "2000");
var movie3 = new movie("The Third Matrix", "2000");
// we're going to declare our array as 'Observable', which will
// allow it to notify the view of any changes
this.movies = ko.observableArray([movie1, movie2, movie3]);
Just like we made our 'name' property in our movie class above an Observable, we will do the same with our array holding all of the movies. By doing this any additions and/or deletions to the array will notify any bound elements to the change. These notifications can also cascade down. So changes to the name property in our movie class, will also get propagated to our observable array.
// adds a new movie object into our array collection
this.addMovie = function () {
self.movies.push(new movie("unnamed", ""));
};
The function that we will call to add new elements into our array. Because our array is now an Observable property, this would update our View to reflect the additions.
// this is a computed property, which we can define
// to be anything in the callback function
// it's powerful because since our movie array is Observable
// it will update this value automatically as well
this.counter = ko.computed(function () {
return self.movies().length;
}, this);
};
// this makes this a Knockout.js application
ko.applyBindings(new MoviesViewModel());
</script>
Let's finish off our ViewModel with a Computed Property. Our counter property above is computed based on the callback function that we define it to be. In this case, it will be set to the length of our movie array. And because it is Observable, whenever we add items to our array, this value will be updated.
Definition: Computed Property - these are observable (i.e., they notify on change) and they are computed based on the values of other observables.
At this point you should have a working Knockout.JS example. It won't do anything yet, since we haven't written any HTML yet, so let's get started on our View.
Create A View
Views are your front-end code mixed in with the many different Knockout binding directives that they offer. The Knockout bindings are what keeps our views updated based on model changes. There are various kinds of view directives that you can add to your front-end. Anything from binding variables to a DOM element to grabbing list and arrays and rendering them in pre-defined templates. Here's a quick view broken down by the important elements:
<div style="float:left;">
<table>
<tbody data-bind="foreach: movies">
<tr>
<td><input data-bind="value: name" /></td>
</tr>
</tbody>
</table>
</div>
The data-bind attribute binds our table to our model property. The tbody element above is binding to the movies array by using the foreach directive which takes care of lists and collections, and in our case our movie array. Anything below that is repeated for every item in the collection. The td is binding the name property of the current movie it's iterating through and because it is an input field, we prefix it with "value:". This will render an input field with it's value set to the name of the movies in our array.
<div style="float:left;margin-left: 10px;">
<table>
<tbody data-bind="foreach: movies">
<tr>
<td><div data-bind="text: name"></div></td>
</tr>
</tbody>
</table>
This part is just for display purposes so that you can see how the data stays in sync across the view and model.
</div>
<div style="clear:both;"></div>
<div data-bind="text: counter"></div>
<input type="button" value="add" data-bind="click: addMovie" />
Here we have our computed property, which will keep track of the length of our object array, and we're going to bind our button's click event to the addMovie function that we defined in our ViewModel. And that's it pretty much. We declared our data properties, set them to observable, then bound them to DOM elements on the front-end. That's what Knockout.JS can do for you. It handles the sometimes tedious process of having to maintain state on our html to coincide with javascript events going on in the background.
Here's a look at our application so far. Making any updates to the left side input fields, will automatically update the bound fields on the right of the changes and will update them accordingly. Our computed 'counter' field at the bottom stays up to date and reflects the current number of records being displayed.
Overview
- We created our ViewModel with observables and computed properties
- We defined data to work with our application
- We set a few of the properties to Observable, so that they could stay in sync with the View
- We wrote some functionality in our ViewModel to handle adding more records to our movie array.
- We added declarative binding tags to our html to bind elements to our model
That's just a quick glimpse at a very simple use case, but it's a big part of why Knockout.js can be beneficial to a website. Particularly to a very dynamic single page application or ajax heavy site. There's tons and tons more that you can do with Knockout.js so feel free to play around with the library and to come up with cool ways to implement it into your websites. I hope this helps anyone who's brand new to Knockout and just wants to take it for a test drive to see if it's right for them. Happy Coding!
The Full Example For Your Copy/Paste Pleasure
<div style="float:left;">
<table>
<tbody data-bind="foreach: movies">
<tr>
<td><input data-bind="value: name" /></td>
</tr>
</tbody>
</table>
</div>
<div style="float:left;margin-left: 10px;">
<table>
<tbody data-bind="foreach: movies">
<tr>
<td><div data-bind="text: name"></div></td>
</tr>
</tbody>
</table>
</div>
<div style="clear:both;"></div>
<div data-bind="text: counter"></div>
<input type="button" value="add" data-bind="click: addMovie" />
<script type="text/javascript" src="Scripts/knockout-3.2.0.js"></script>
<script type="text/javascript">
function movie(name, year)
{
var self = this;
self.name = ko.observable(name);
self.year = year;
}
var MoviesViewModel = function () {
var self = this;
var movie1 = new movie("The Matrix", "2000");
var movie2 = new movie("The Matrix Reloaded", "2000");
var movie3 = new movie("The Third Matrix", "2000");
this.movies = ko.observableArray([movie1, movie2, movie3]);
this.addMovie = function () {
self.movies.push(new movie("unnamed", ""));
};
this.counter = ko.computed(function () {
return self.movies().length;
}, this);
};
ko.applyBindings(new MoviesViewModel());
</script>
Walter Guevara is a Computer Scientist, software engineer, startup founder and previous mentor for a coding bootcamp. He has been creating software for the past 20 years.