<

Go Premium for a chance to win a PS4. Enter to Win

x

Introducing the Knockout JavaScript Framework (MVVM)

Published on
9,201 Points
2,101 Views
1 Endorsement
Last Modified:
Rob
I'm an enthusiastic Web Solutions Provider that designs and creates a website YOU want for you and your customers

Introduction


Knockoutjs (Knockout) is a JavaScript framework (Model View ViewModel or MVVM framework).  

The main ideology behind Knockout is to control from JavaScript how a page looks whilst creating an engaging user experience in the least complicated way as possible.

There are also other frameworks that essentially do the same thing in their own way. Find what works for you. React is what Facebook is built on and is very popular. AngularJS has also gained a lot of momentum and worth checking out.

Knockout takes a basic HTML structure and applies logic (written by you) to control the insertion and removal of HTML elements. This keeps the markup simple and straightforward, especially when it comes to repetitive content.

It's incredibly easy with Knockout to create dynamic interactive web pages with simplified markup. I've seen markup get out of control when creating content both from the server side (e.g. using PHP) or via the client side (e.g. using jQuery), making it virtually impossible to troubleshoot issues with layout and styling. 

I come across many questions in the web dev topic areas that are related to issues with the asker's website. Invariably this starts with them being told to fix their markup and validate it through the The World Wide Web Consortium's (W3C) Validation service.  When a site doesn't validate, it is not guaranteed to work on all devices and browsers, let alone with Knockout.

Knockout allows you to represent your markup as an object structure in JavaScript (called a View-Model) and as that object structure changes, the HTML is updated automatically to reflect those changes.

As a language, JavaScript is a lot easier to create logic with (given it’s designed for programming), whereas HTML, being a subset of XML is just tags for encapsulating content. There's really no logic or programming to speak of so it makes sense to keep this as simple as you can and leave the hard stuff to JavaScript.

The expectations with the web are higher than ever. With the emergence of mobile apps that deliver real time interaction, the expectation is that a website should have the same responsiveness. Single page applications deliver a richer user experience, as the interaction is at a speed similar to a native desktop application but through a far more accessible method a web browser.  

Knockout will only run on the page it's loaded from so the logic is specific to what's happening on that particular page. However, It is possible to pass and store almost any kind of information in the view model when the page loads or at anytime through the life cycle of the page (via Ajax) to implement a required state.

It's important to note that Knockout is self-reliant in that it doesn't require jQuery or any other framework but integrates very nicely with them.  I quite often use Knockout with jQuery and Bootstrap as you’ll see later in the example.

So when to use Knockout?  Read on...

"To be or not to be" - When to use Knockout in your app
A site can become "bloated" very quickly, when including many different frameworks, libraries and styles. Each one will have to be downloaded by the client when they visit your site making the time taken to load your site longer than necessary. Nowadays, the expectation is for information to be presented near instantaneously. If your website is vital to your company's marketing strategy the having a fast snappy site will give you an edge over your competitors. Caching helps to a certain extent but should not be relied on.

That said, Knockout should only be used when logic needs to be applied to the markup on your page. What kind of logic am I talking about? Knockout has the ability to store information and uses your model to model that information into your webpage. If there's no need to create a state machine (http://en.wikipedia.org/wiki/Finite-state_machine) then you don't need it.

What about jQuery? Yep, jQuery is great for controlling logic of individual elements but it starts to get complex when all those elements are related to each other. What makes Knockout different is observables. Observables are essentially just variables within the view model. When one of these observables changes, Knockout checks the rest of the view model to see if anything is affected and updates the necessary items including the html elements on your page.

For example, if your users are just filling out a form on a page, where no thought is needed, then there's no reason to use Knockout. If, on the other hand, the form needs to add, remove, show, hide, sort or filter content based on the selections or content entered by the user of your webpage, then Knockout could be for you.

Consider flowcharting the actions needed on each page; this will tell you very quickly whether Knockout is needed. If you have a very straight forward flowchart

e.g. [start] => [fill in data] => [submit]

then there's nowhere Knockout can add value. 

It's when questions in your flowchart start cropping up (e.g. is the user already a member?), or when there are multiple paths based on a single action (e.g. choosing a category of product) that the power of Knockout comes into play. 
 

How does Knockout work?



data-bind syntax


Basically, for each element you are going to interact with, the “data-bind” attribute is added.  

e.g. This will update the contents of the div to have the text “What a very nice day”
<div data-bind="text:'What a very nice day'"></div>

Open in new window


This data-bind attribute is a way for your knockout JavaScript to not only know which elements it’s dealing with but also what properties to manipulate. This is the link between the element and the Knockout view model you’re about to create. When you update the view-model object in JavaScript, it will know what elements to check because of the data-bind attribute. If that update has affected any property of an element then the element is updated to reflect the new value.

Knockout will often manipulate the standard attributes of an element (such as value for an input element) so these are not necessary and will be ignored if they are again linked to in the data-bind attribute.

e.g.
<input type="text" name="firstname" value="Robert" data-bind="value: firstname" />

Open in new window



This will not make much sense now but the “value” of the text field will be whatever the latest value for the variable “firstname” is in your view-model.  Read on, it’ll make more sense soon.


view-model

The view-model is just a JavaScript object:
 
var vm = { };

Open in new window



We add properties, variables and functions to this object to give it definition, before handing it over to Knockout to link everything together.

To make it so that Knockout can understand what we’re doing, we create our variables using the observable functions from Knockout.  We’re essentially telling Knockout that these values are going to change so keep an eye out for them and update everyone as required.

So later, we’ll build a form that captures the name and gender of the user and shows different options based on the gender selected.  These values are recorded in your view-model and used as required.
 
var vm = {
    firstname: ko.observable(),
    lastname: ko.observable(),
    gender: ko.observable(),
};

Open in new window



If we were to pass a value to the observable() function, that would be the initial value for that variable, which would update the linked element in your HTML.

On our form, we’re going to have a simple dropdown list for selecting the gender. I realise this could also be a group of two radio buttons but for this example, I’m showing you how easy it is to create a dropdown and use it’s value in real time.  This especially becomes more prevalent with large data sets or getting data using AJAX (think country names for instance and creating a select box for that).

So we add our available options to our view-model as an array using the observableArray() function.
 
vm.genders = ko.observableArray([“male”,”female”]);

<select class="form-control" name="gender" id="" data-bind="options: genders, optionsCaption: '-select-', value: gender">

Open in new window


The syntax of the data-bind in the select element is described as:

options: this is the variable from the view-model that has the options to display in the dropdown
optionsCaptions: this is just what to show when there hasn’t been a selection yet. This is also shown when the gender observable is set to null or “”
value: this is the view-model variable that holds whatever has been selected.

Note that there are no option elements for the select element. They are created automatically by Knockout. With larger dropdowns with more options, this can make your HTML easier to read.

To capture the name, we add the first name and last name variables to the data-bind attribute of each
 
<input class="form-control" type="text" data-bind="value: firstname"/>
<input class="form-control" type="text" data-bind="value: lastname"/>

Open in new window


Now it gets interesting.  

This form is about capturing what printed magazines you may like depending on your gender. To do that, we detect when the gender is changed we display only the relevant magazine names.

The magazines are stored in the view-model like so:
    vm.magazines: ko.observable({
      "m": ["Zoo","Men\'s Health","Golf Digest","Inside sport"],
      "f": ["Cleo","Dolly","Womens Weekly"]
    }),

Open in new window


The magazines observable contains an object with a “m” and a “f” property that each contain an array of magazine names.

Here is the male section:
 
<div data-bind="visible: gender()=='male'">
              <fieldset>
                <legend>Magazines for Men</legend>
                <div data-bind="foreach: magazines().m">
                  <input type="checkbox" name="mags[m]" data-bind="value: $data, checked: $parent.selectedMags, attr: {id: 'm'+$index()}"/>
                  <label data-bind="attr: {for: 'm'+$index()}, text: $data"></label>
                </div>
              </fieldset>
 </div>

Open in new window


The whole section uses a visible property, internal to Knockout that allows to showing or hiding elements based on any logic the view-model has access to.  In this case, this section is only visible when the gender observable is “male”.

The next part dynamically creates the magazine names with a corresponding checkbox for the user to select. It does this via the foreach binding such that any child element of where this is applied is duplicate for each element in the array. As you can see from the example, a new input element will be created for every magazine.

foreach: magazines().m
magazines() returns an object with m and f properties, each containing an array so magazines().m returns the male magazines array.

These elements are within the loop and need slightly different syntax, where scope has to be taken into account.

$data, $parent, $index are special Knockout observables that represent data specific to the loop.
$data is the current value of the array
$index is the corresponding index that we’re up to looping through the array
$parent is making sure that we are accessing the right variable, in case the array we’re iterating over has an element with the same property name.
 
<input type="checkbox" name="mags[m]" data-bind="value: $data, checked: $parent.selectedMags, attr: {id: 'm'+$index()}"/>
<label data-bind="attr: {for: 'm'+$index()}, text: $data"></label>

Open in new window


This whole section is repeated for female.

I’ve also added a section to dynamically show what you’ve selected. For this, we’ll use a computed observable.  Fancy name for something that just combines other observables into a more readable format:
 
<div data-bind="html: myDetails"></div>

Open in new window


In the view-model, myDetails is created like this and just joins all the values together in a readable format:
  vm.myDetails = ko.pureComputed(function() {
    // concatenate the first and last names
    var name = vm.firstname() + " " + vm.lastname();
    // put brackets around the gender for displaying
    var gender = (vm.gender()) ? "(" + vm.gender() + ")" : "";
    // return the name and gender together as one string
    return name+" "+gender+" "+vm.selectedMags().join();
  });

Open in new window


A finishing touch is to make sure we reset the magazine selections if the gender is changed.  To do that we capture when the gender is changed (via a subscribe event) by the user and we remove all the elements from the selectedMags array:
vm.gender.subscribe(function() {
    vm.selectedMags([]);
  });

Open in new window


Example - Choose your favourite magazines


In this example I’m going to show you how to create a dynamic form that changes as you enter data.  We’ll be using jQuery to simplify our JavaScript coding along with Bootstrap to lay out the form nicely.

For those that want to see how it works, skip to the jsbin demo: http://jsbin.com/waguli/1/edit?html,js,output

Include Knockout in the header of your page (after jQuery and Bootstrap) by either downloading it or getting it from a CDN (both ways are described here)
 
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1">
<script src=”https://cdnjs.cloudflare.com/ajax/libs/jquery/2.1.4/jquery.min.js”></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.4/css/bootstrap.min.css" rel="stylesheet" type="text/css" />
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.3.0/knockout-min.js"></script>

Open in new window



Quick Side Step - Bootstrap Intro

For those that do not know Bootstrap, I recommend using it where ever you can to make your layout as easy as possible.

As a quick intro, everything in Bootstrap is laid out in a grid of 12 columns. To make it work you start with a “container”, that contains a number of “row” elements. Within each row you set up the number of columns you want. Visualize your page and divide it into a layout that is made up of rows and columns.

Every column class starts with “col-” followed by “xs-”,”sm-”,”md-” or ”lg-”, followed by the number of columns (out of 12) that you want your column to take up.  The “xs-”,”sm-”,”md-” or ”lg-” just signifies how many columns you want that element to take up at different screen widths. Bootstrap is smart enough to work out how big the visible space is on the screen and will act accordingly. e.g. col-xs-6 means you want to take up 6 columns when the screen size is below 768px. See Bootstrap for more info: http://getbootstrap.com/css/#grid-options.


Back to the code...

I’ve used a fieldset and legend tag to give the page a nicer feel

<body>
    <div class="container">
      <div class="row">
        <div class="col-xs-6">
          <fieldset>
            <legend>Your Details:</legend>
            <div data-bind="html: myDetails"></div>
          </fieldset>
        </div>
        <div class="col-xs-6">
          <form action="">
            <fieldset>
              <legend>Enter your details:</legend>
              <div class="row">
                <div class="col-xs-3"><label>First Name</label></div>
                <div class="col-xs-7">
<input class="form-control" type="text" data-bind="value: firstname"/>
    </div>
                <div class="col-xs-12">&nbsp;</div>
                <div class="col-xs-3"><label>Last Name</label></div>
                <div class="col-xs-7">
                     <input class="form-control" type="text" data-bind="value: lastname" />
    </div>
                <div class="col-xs-12">&nbsp;</div>
                <div class="col-xs-3">
                  <label>Gender</label>
                </div>
                <div class="col-xs-7">
                  <select class="form-control" name="gender" id="" data-bind="options: genders, optionsCaption: '-select-', value: gender">
                  </select>
                </div>
              </div>
            </fieldset>
            <br/>
            <div data-bind="visible: gender()=='male'">
              <fieldset>
                <legend>Magazines for Men</legend>
                <div data-bind="foreach: magazines().m">
                  <input type="checkbox" name="mags[m]" data-bind="value: $data, checked: $parent.selectedMags, attr: {id: 'm'+$index()}"/>
                  <label data-bind="attr: {for: 'm'+$index()}, text: $data"></label>
                </div>
              </fieldset>
            </div>
            <div data-bind="visible: gender()=='female'">
              <fieldset>
                <legend>Magazines for Women</legend>
                <div data-bind="foreach: magazines().f">
                  <input type="checkbox" name="mags[f]" data-bind="value: $data, checked: $parent.selectedMags, attr: {id: 'f'+$index()}"/>
                  <label data-bind="attr: {for: 'f'+$index()},text: $data"></label>
                </div>
              </fieldset>
            </div>
          </form>
        </div>
      </div>
    </div>
  </body>

Open in new window



basic-form.JPG

Knockout view-model

To ensure all the elements have loaded correctly and the DOM is ready, we wrap our code in jQuery’s initialization function.

The view model is just a JavaScript object. You can add all your properties when you create the object, or you can add them after (as shown below)
$(function() {
  // create the basic view model object with the required variables (observables)
  var vm = {
    firstname: ko.observable(""),
    lastname: ko.observable(""),
    gender: ko.observable(""),
    genders: ko.observableArray(["male","female"]),
    magazines: ko.observable({
      "m": ["Zoo","Men\'s Health","Golf Digest","Inside sport"],
      "f": ["Cleo","Dolly","Womens Weekly"]
    }),
    selectedMags: ko.observableArray([])
  };

  // create a computed observable that returns the data nice and pretty (added after the initial object was created)
  vm.myDetails = ko.pureComputed(function() {
    var name = vm.firstname() + " " + vm.lastname();
    var gender = (vm.gender()) ? "(" + vm.gender() + ")" : "";
    return name+" "+gender+" "+vm.selectedMags().join();
  });
  
  // A subscribe function is called when the value for an observable changes
clear any selected mags if we change gender
  vm.gender.subscribe(function() {
    vm.selectedMags([]);
  });

  // bind the model to the view
  ko.applyBindings(vm);
});

Open in new window


Running your example, you’ll see the names you enter automatically appear in the “Your Details” section. Changing gender will show you different options, which will also appear under “Your Details” as the selections are made.


A Last Point: Search Engine Optimization (SEO)

The point should also be made that your Search Engine Optimization (SEO) strategy should go hand in hand with Knockout. It's important that your site doesn't rely on Knockout creating all the content on the page. There still needs to be the basic markup there to ensure your page could still render without it and not affect SEO or your SERP. 


Summary

Knockout can be very powerful and useful when applied properly. It gives you the framework to build a robust model around your webpage but I only advocate using it when the application calls for it.

It certainly has a lot of possibilities:
- Filter and sort content on the page based on any kind of attribute such as category or price.
- First step validation of any data a user has entered into a form. 
- Create large dropdown lists with minimal HTML in your page
- Makes it easy to show your data via computed observables

and so on...

Careful consideration should be made to each and every additional plugin, framework and style to be included in your site. By no means is this article advocating to use a MVVM framework on all or any of your pages, I'm giving you the information on when to use a MVVM framework and when you could be unnecessarily bloating and slowing down your site.


Further Reading


Framework Documentation

Knockout: http://knockoutjs.com/documentation/introduction.html
Bootstrap: http://getbootstrap.com/css/ 
jQuery: http://api.jquery.com/ 


Relevant EE Questions

http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Q_28503713.html
http://www.experts-exchange.com/Programming/Languages/Scripting/JavaScript/Jquery/Q_28616759.html





1
Comment
Author:Rob
2 Comments
 
LVL 3

Expert Comment

by:mmoore
Really interesting stuff! So many technologies so little time.
A couple of typos you may wish to correct. "It incredibly easy" "know no only".
0
 
LVL 43

Author Comment

by:Rob
Thanks @mmoore :)
0

Featured Post

VIDEO: THE CONCERTO CLOUD FOR HEALTHCARE

Modern healthcare requires a modern cloud. View this brief video to understand how the Concerto Cloud for Healthcare can help your organization.

Join & Write a Comment

Any person in technology especially those working for big companies should at least know about the basics of web accessibility. Believe it or not there are even laws in place that require businesses to provide such means for the disabled and aging p…
Video by: Mark
This lesson goes over how to construct ordered and unordered lists and how to create hyperlinks.
Suggested Courses

Keep in touch with Experts Exchange

Tech news and trends delivered to your inbox every month