At Least it’s Not MVC
Looking at the Knockout home page, the key concepts it lists are:- Declaritive Bindings
- Automatic UI Refresh
- Dependence Tracking
- Templating
Lick it, Lick it Good
The demo application is a simple, yet kind of disgusting, app called LinkLicker. The idea is that links are submitted and you lick the ones you like. Yes, I am a 4 year-old. The server-side of this application is both a Rails app and irrelevant. Your server side application simply needs to spit back JSON, so it can be Sinatra or Rails or Cuba or whatever. I shant bore you with the details of the web app, but they are in the github repository if you simply must know. The app shows a form that allows links to be submitted, a list of submitted links, and a list of links that have been licked by the current user. When a link has been licked, it’s icon changes. This, I am sure, is the next FacebookTwitter.Setup
You will need the Knockout code available in your web app to make this work. I’ve put mine in the vendor directory, along with the most commonly used Knockout plugin. This plugin is the Knockout Mapping plugin , and it takes all the hard work out of mapping JSON from the server to JavaScript objects on the client. Note: I won’t really cover the Mapping plugin in this article, but the important bit is it creates observables out of all our JSON properties when it maps the objects. You’ll see what observables are shortly. Structurally, I like to put each “model” class in it’s own file on the client. LinkLicker really only has one model: Link. I also like to have a global class that represents my application that I call, unremarkably, App. The final directory sturcture for the JavaScript looks like:UI
The UI for LinkLicker was driven heavily by my desire to do as little as possible while still getting the Knockout concepts across. As I mentioned above, there is a form, as well as three lists of links. The three lists are All Links, New (meaning, unlicked), and licked links. As you may have surmised, a link can be in more than one of these lists at any given time. The screenshot of the UI is below:Bootstrap
Bootstrapping LickLinker consists of binding to the form and creating our three lists. We’ll make all of these properties on our App. Easy stuff.var App = function() {
this.newLink = ko.observable(new Link());
this.mylinks = ko.observableArray();
this.lickedLinks = ko.observableArray();
this.allLinks = ko.observableArray();
};
You are likely wondering what all this observable madness be. When you make a property an observable, it becomes a super-powerful notifier of changes to that property. Knockout will keep track (as best it can, and it’s pretty good at it) of things that are interested in your property and notify then when it changes. The Observables docs on the Knockout site are great, and there are interactive tutorials to use as well.
In short, and if you can think ahead, when we bind our UI (form) to the newLink it will then notify our form of changes and vice-versa.
The other three properties are observableArrays. These are, conceptually, exactly the same as observables, except that they work on a collection. An observableArray will track what objects are in the array, and it has many helper methods to add and remove these objects. You can easily pop or push items onto an observableArray, slice it, get its length, reverse it, and many other methods you’d expect.
Again, since observables and observableArrays are notifiers of change, when we bind the lists in our UI to these lists of links, everything will be updated to reflect the latest state. Hopefully, this concept is sinking in.
The Model
The model for LinkLicker is just a link and it has 3 properties: url, description, and lickedByCurrentUser.The Form
Knockout is all about data-binding, remember? As such, we can bind a new Link to the form and, then, as the user enters data it will populate this newly bound Link. In the code snippet above, we create our newLink property as an observable. Now, to bind it to the form:<form id="create_form" class="one-third column alpha" data-bind="submit: createNewLink">
<!-- ko with: newLink -->
<label for="link_url">Link:</label>
<input id="link_url" type="text" name="linkhttps%3A%2F%2Feditor.sitepoint.com" data-bind="value: url" />
<label for="description">Description:</label>
<input id="link_description" type="text" name="link[description]" data-bind="value: description" />
<!-- /ko -->
<input type="submit" value="Add" /></form>
In the above snippet, focus on the <!-- ko with: newLink -->
line. Knockout offers a couple of different ways to bind parts of the DOM to the view model. This example uses “containerless bindings”, which means, it doesn’t create a DOM element for that declaration.
The other way to do it is to create a DOM element (such as a div
) and add a data-bind
attribute specifying the template. There are loads of examples on the Knockout site of the latter if you are confused.
That ko with: newLink
tells Knockout to find the newLink property of whatever this view is bound to and scope everything within the block to it. This should be a bit clearer if you look at the input
tags. Each input tag has a data-bind
attribute that binds the value of that input to a property on newLink.
The Ties the Bind
With our UI in place, we need some mechanism to bind it to our view model. In this app, the view model is our global App class on the client. The App is bound to the view when the page is loaded, as shown here:$(function(){
App.globalApp = new App();
ko.applyBindings(App.globalApp);
App.globalApp.loadLinks();
});
Easy peasy.
Filtering a list can be done by creating two “computeds” (formerly dependentObservables). Here, let me show you:
this.allLinks = ko.observableArray();
this.lickedLinks = ko.computed(function() {
return this.allLinks().filter(function(link) {
return link.isLicked();
});
}, this);
this.newLinks = ko.computed(function() {
return this.allLinks().filter(function(link) {
return !link.isLicked();
});
}, this);
A computed
is an observable that depends on the values of other observables. You can see that we filter the allLinks observableArray based on the value of isLicked() for each link. The second argument to the computed (this
) is the context to bind the function.
Computeds are enormously helpful and you’ll find yourself using them everywhere.
Extending KnockoutJS
While Knockout has many, many bindings that you can use, such as text, html, css, visible, you will find occasions that require a new binding. Writing a custom binding is very simple. As an example, I am going to write ahidden
binding which is the opposite of the visible
binding. It’s is below:
ko.bindingHandlers.hidden = {
update: function(element, valueAccessor){
var value = ko.utils.unwrapObservable(valueAccessor());
ko.bindingHandlers.visible.update(element, function() {
return !value;
});
}
};
In the binding, I grab the value of the element being bound, and then pass it to the existing visible
binding. The result is negated and returned.
Obviously, this is a trivial binding, but you can get very complex if needed. Google will show you bindings for jQuery UI elements, for example, along with thousands of other custom bindings that the community has authored.
Momma Said Wrap This Up!
There were many other aspects of Knockout I would like to cover, but I think we’ve taken a nice look across many of it’s features. Hopefully, when you are evaluating JavaScript frameworks, this article will help you make a decision. Remember, the entirety of the LinkLicker source is yours to browse, use, mock, whatever on github. If you have questions on LinkLicker or Knockout, hit me in the comments. Thanks for reading!Frequently Asked Questions (FAQs) about Knockout.js
What is the main difference between Knockout.js and other JavaScript libraries?
Knockout.js is a JavaScript library that helps developers to create rich, responsive display and editor user interfaces with a clean underlying data model. Unlike other JavaScript libraries, Knockout.js uses a Model-View-ViewModel (MVVM) design pattern where the model refers to the data, the view is the visual representation of the data, and the ViewModel is the intermediary between the model and the view. This pattern allows for a clear separation of concerns, making your code easier to manage and understand.
How does Knockout.js handle data binding?
Knockout.js uses declarative bindings to connect parts of your UI to your data model. This means you can easily define complex relationships between view components and data models without having to write a lot of boilerplate code. You can bind data to attributes, text, and even CSS classes. Knockout.js automatically updates the UI when your data model changes, and vice versa.
Can Knockout.js work with other JavaScript libraries?
Yes, Knockout.js is designed to be very flexible and can work alongside other libraries or frameworks. You can use it with jQuery, Bootstrap, or even AngularJS. It doesn’t make any assumptions about the rest of your technology stack, so it can easily be integrated into any web project.
Is Knockout.js suitable for large-scale applications?
Knockout.js is lightweight and flexible, making it suitable for both small and large-scale applications. Its MVVM pattern promotes a clean and maintainable codebase, which is crucial for large-scale applications. However, for very complex applications, you might want to consider using a more robust framework like AngularJS or React.
How does Knockout.js handle events?
Knockout.js provides a simple and consistent way to handle events. You can easily bind event handlers to view elements using the ‘data-bind’ attribute. Knockout.js supports all standard JavaScript events, and you can even create custom events if needed.
What is the learning curve for Knockout.js?
Knockout.js is relatively easy to learn, especially if you’re already familiar with JavaScript and jQuery. Its API is straightforward and well-documented, and there are plenty of tutorials and examples available online. However, understanding the MVVM pattern might take some time if you’re new to it.
Can Knockout.js be used for mobile app development?
While Knockout.js is primarily designed for web development, it can also be used in mobile app development in combination with tools like PhoneGap or Cordova. However, for complex mobile apps, you might want to consider using a dedicated mobile app framework.
How does Knockout.js handle form validation?
Knockout.js itself doesn’t provide any built-in form validation features. However, you can easily implement form validation using custom bindings or third-party libraries. There are also several Knockout.js plugins available that provide advanced validation features.
How is Knockout.js tested?
Knockout.js provides a test suite that you can use to test your application. The test suite includes a variety of tools and libraries that make it easy to write and run tests. You can also use third-party testing frameworks like Jasmine or Mocha with Knockout.js.
What is the future of Knockout.js?
Knockout.js is actively maintained and continues to be a popular choice for web development. However, with the rise of more modern frameworks like AngularJS and React, it’s hard to predict the future of Knockout.js. Nevertheless, its simplicity, flexibility, and robustness make it a viable option for many web development projects.
Glenn works for Skookum Digital Works by day and manages the SitePoint Ruby channel at night. He likes to pretend he has a secret identity, but can't come up with a good superhero name. He's settling for "Roob", for now.