KnockoutJS by Example

4 (1 reviews total)
By Adnan Jaswal
    What do you get with a Packt Subscription?

  • Instant access to this title and 7,500+ eBooks & Videos
  • Constantly updated with 100+ new titles each month
  • Breadth and depth in over 1,000+ technologies
  1. Free Chapter
    Getting Started
About this book

KnockoutJS By Example is a project-based guide that introduces the key features and concepts of knockout.js. It helps you create an application skeleton and a Hello World application. You will develop a To-Do list application that aims to show the basic features of knockout.js in action, such as data binding and observables, following which you will develop a dynamic online customer registration form that captures and validates customer information. This book will further walk you through developing a customer banking portal, which demonstrates the use of knockout.js with components such as navigation bars, tabs, carousels, master details view, panels, forms, and wizards. You will also discover how to use token-based authentication and authorization to secure the customer banking portal, and move on to creating an editable products grid with CRUD operations. Finally, you will explore how to use the Google Maps API with knockout.js.

KnockoutJS By Example will not only leave you with a basic understanding of knockout.js fundamentals but also take you through some of the advanced features. It will help you get a web application up and ready instantly.

Publication date:
September 2015
Publisher
Packt
Pages
268
ISBN
9781785288548

 

Chapter 1. Getting Started

Knockout is an open source JavaScript library that lets you develop rich, interactive, and modular web applications. It does this in a manner that reduces complexities of JavaScript and HTML development and allows us to develop highly scalable, testable, and maintainable web applications.

Knockout provides the ability to bind HTML elements to a data model. The binding is two-way, which means that any change to the data is reflected in the HTML elements and any change to the HTML elements is reflected in the data. Knockout implements two-way binding using the Model-View-View Model (MVVM) design pattern. You will learn more about this pattern in the next section.

Knockout is a pure JavaScript library and is not dependent on other low-level JavaScript libraries such as jQuery or Prototype. Libraries such as jQuery can be used in conjunction with Knockout to provide richer features to your application such as making AJAX calls, providing animation to HTML elements, or providing event handling for custom user interface components.

Knockout supports all major browsers. A list of supported browsers can be found on the Knockout's website at http://knockoutjs.com/.

This chapter covers the following topics:

  • Understanding the MVVM design pattern: We will explore the MVVM pattern and how it is implemented using knockout.js

  • Key features of Knockout: We will look at the key features of knockout.js

  • Understanding the module pattern: We will explore the module pattern and see how it can be used to give structure to your Knockout application

    • Building your first application: We will build the first application and learn where to download knockout.js from and how to set up the development environment

    • Defining data model and applying data bindings: We will also learn the basics of defining the data model and applying data bindings

    • Applying styles to our application using Bootstrap

  • Taking a look at some useful resources

 

Understanding the MVVM design pattern


Knockout implements the MVVM design pattern. It is imperative to understand the basic concept behind MVVM before we dive into Knockout. This will help us grasp how two-way binding is implemented in Knockout and what are its benefits.

MVVM is a design pattern that lets you decouple your UI elements from your application logic and data. It lets you define data binding to link the UI elements to the data. The data bindings provides loose coupling and keeps the data in sync with the UI elements. The MVVM pattern provides clear separation of concerns between UI elements, application logic, and the data.

The three main components of this pattern are:

The model

The model is a domain object, which holds the data and domain-specific logic. An example of a model could be of a contact in an address book, containing contact ID, name, and phone number. The following is an example of a contact model in JavaScript:

var contact = {
    id: 1,
    name: 'John',
    phoneNumber: '00 11 000000'
};

The model should not contain any application logic such as service calls. The model can contain business-specific logic that is independent of the UI. Separating business logic from UI makes the code more maintainable and testable.

Note

The contact object in the given example is declared as an object literal, which uses Java Script Object Notation (JSON). It is important to familiarize yourself with this notation if you are not. You can find more on this topic at http://json.org/.

The view model

The view model holds the model and any application logic such as adding or removing data or making service calls to retrieve or storing data from server-side data repositories. The following is an example of a view model that holds a contact and provides method to retrieve the contact from a server-side data repository:

var contactViewModel = {
  var contact = {
    id: 1,
    name: 'John',
    phoneNumber: '00001111'
  };

  Var retrieveContact = function (){
    /* logic to retrieve contact form server side data repository */
  };

  Var updateContact = function (newPhoneNumber){
    /* logic to update the contact with new phone number */
  };
};

The view model itself does not have any concept of the HTML elements, button-click event, or how the data in the model should be displayed. It simply holds the data model and a set of actions in the form of functions that manipulate the data.

In the preceding example, contactViewModel holds the contact model. It also has two functions that are used to manipulate the contact model. The retrieveContact function, which is used to retrieve a contact from the server-side, and the updateContact function, which is used to update the contact with a new phone number.

The view

The view is what the end user sees and interacts with on the screen. The view is a representation of the data contained in the model. The view also provides a mechanism to interact with the model. In our example, the model contains a contact. The view can display the contact and provide HTML elements such as buttons to retrieve and update the contact.

In Knockout, the view is the HTML with data bindings that link the view to the view model. The following HTML displays the contact name and phone number:

The phone number for <span data-bind="text: name"></span> is <span data-bind="text: phoneNumber"></span>
<button data-bind="click: retrieveContact">Load Contact</button>

In the preceding example, the contact name and phone number are being displayed using the text binding. Click binding is used to link the Load Contact button to retrieveContact function in our view model. We will explore bindings in much more detail later on.

The following figure depicts the relationship between view, model, and view model:

In the preceding figure, the view model holds the state of the model and provides behavior to the view. The view binds with the model and this keeps the view and the model in sync. The view also binds with the view model for operations, for example, the behavior to load contacts. The view model uses the model to manipulate the data. For example, the retrieveContact function retrieves a contact and sets it in the model.

 

The key features of Knockout


In this section, we will explore some of the key features of Knockout. It is important to understand these features and their basic syntax before we dive into working examples.

Declarative bindings

Knockout provides a way to link the model and view model with the view using a declarative binding mechanism. The bindings are declared in HTML. The following is an example of a simple text binding:

The phone number for <span data-bind="text: name></span> is 0000111

Let's explore the data binding syntax. The bindings are declared using the data-bind attribute on an HTML element. The value of this attribute has two elements, which are separated by a colon. The two elements are name and a value.

The name specifies the type of binding. This should match a registered binding handler. A binding handler is an object that contains the code to bind the HTML element to our model. Knockout provides a number of useful binding handlers. A custom binding handler can be created and registered with Knockout if none of the out-of-the-box handlers meet your specific requirements. In most cases, the out-of-the-box handlers will do the job.

Tip

Knockout will ignore the binding without any error if the name does not match any of the registered binding handlers. Check the name if the binding does not appear to be working!

The value can be an attribute from the model or any valid JavaScript expression. In the preceding example for contact, we used a text binding with the value, and the name. The value in this case comes from the model.

Here is an example of a binding using a JavaScript expression:

The phone number for <span data-bind="text: retrieveContactName()></span> is 0000111

In this example, the text value is evaluated by calling the retrieveContactName JavaScript function.

Note

Knockout will throw an error and stop processing the bindings if the value is an invalid expression or if it references an undefined variable.

You can include multiple bindings in the data-bind attribute, with each binding separated by a comma. Adding a visible binding to our weather forecast example will make it look similar to this:

The phone number for <span data-bind="text: name, css: favourite"></span> is 0000111

In the preceding example, the text for the span element will come from the name attribute in our model. The css binding will determine the CSS class to be applied, based on the favourite attribute in our model for the span element.

Tip

You can include any number of spaces, tabs, or newlines in your binding syntax. Use this to arrange your bindings to make them more readable!

In more advance usage, the binding can also be a parameter for another binding. Here is an example in which the template binding takes the foreach binding as a parameter:

<tbody data-bind="template: {name: 'contact-template', foreach: contacts}">

As mentioned earlier, Knockout provides a number of very useful binding handlers that come out of the box. Knockout documentation divides these binding handlers in to three categories:

  • Controlling text and appearance: As the name suggests, these binding handlers control the text and the styling of the UI elements. Examples of binding handlers in this category include text and css. We used these bindings as examples earlier in this section.

  • Flow control: These binding handlers provide control structures such as loops and conditions. The foreach and if binding handlers fall under this category. We will explore these bindings in more detail in the coming sections.

  • Working with form fields: Capturing data with forms is one of the most basic requirements in web applications. Binding handlers in this category provide the functionality to work with form fields. Some of the examples include click, value, and submit binding handlers. We will learn more about bindings in this category in Chapter 3, Creating an Online Customer Registration Form and Chapter 4, Adding Validation to the Customer Registration Form.

Automatic UI refresh

Automatic UI refresh is a very useful feature of Knockout. This feature is based on the concept of two-way binding between the view and view model. Whenever the data in the model changes, it is reflected in the UI. When the input fields in the UI change, it updates the underlying data.

This feature reduces the amount of code and complexity by many folds. Those who are accustomed to writing event handlers in JavaScript to connect data with UI fields and vice versa would surely appreciate this feature. Implementing this in jQuery is definitely easier than developing this in pure JavaScript, but it does not compare with Knockout.

The examples in the previous section for data binding and view produces a one-way binding between the UI and model. Updating the value in the UI field will update the data in the model. To make this binding work both ways, you have to declare the attributes in your model as observables.

Observables are objects that notify their subscribers of any change. Let's apply observables to our contact model:

var contact = {
    id: ko.observable(1),
    name: ko.observable('John'),
    phoneNumber: ko.observable('00001111')
};

By declaring the attributes in your model as observable object, you have activated the two-way binding. You do not have to make any change to the data bindings or view.

Since observables are functions, you can no longer access the attribute in the standard way. To read the value of our name observable, we execute it as a function like this:

contact.name();

To change the value of our name observable to Mary, simply pass the new value as an argument to the name function as follows:

contact.name('Mary');

We mentioned that observables notify their subscribers of any change. When we use observables with data binding, the binding registers itself to be notified when the observable changes value. When the value of the observable changes, the binding automatically updates the UI element.

You can also explicitly subscribe to observables, have observables with values that are computed, or even delay change notification. We will learn more about these later on in the book.

Dependency tracking

Dependency tracking is one of the most exciting features of Knockout. Dependency tracking is based on observables and their subscribers. When Knockout runs for the first time, it evaluates the initial value of each observable and sets up the subscriptions. The subscribers get notified when the observable gets updated with a new value.

Dependency tracking also works for computed observables. Computed observables are the observables that are dependent on one or more other observables. The value of the computed observable is updated every time the value of one of its dependencies changes.

Let's extend our contact model to add first and last name:

var contact = {
  id: ko.observable(1),
  firstName: ko.observable('John'),
  lastName: ko.observable('Jones'),
  phoneNumber: ko.observable('00001111')
};

Now that we have added observables for first and last name, let's add a computed observable for full name:

var contact = {
id: ko.observable(1),
  firstName: ko.observable('John'),
  lastName: ko.observable('Jones'),
  fullName: ko.computed(function() {
    return this.firstName() + " " + this.lastName();
  }, this),
  phoneNumber: ko.observable('00001111')
};

The fullName attribute will return the concatenated first name and last name. Knockout will compute the value of fullName every time the values of either first or last name change.

Dependency tracking allows us to build complex yet sophisticated models that have a set of key attributes and the effects of changing the attributes rippled across the view. Dependency tracking in Knockout is also dynamic. This means that we can have the full name initially dependent on first and last name and then at runtime, add another dependency, say, middle name.

Templating

Templating is another very useful feature of Knockout. Templates are the UI structure that renders a UI, based on the provided elements in the template. Templates are useful when you have a requirement of using the same UI structure multiple times in your application. You should not be expected to cut and paste the same structure every time you plan to use it.

The most basic example of a template is when it is used to repeatedly render a row in a table:

<table>
  <thead>
    <tr>
      <th>Contact</th>
      <th>Phone Number</th>
    </tr>
  </thead>
  <tbody data-bind="foreach: contacts">
    <tr>
      <td data-bind="text: name"></td>
      <td data-bind="text: phoneNumber"></td>
    </tr>
  </tbody>
</table>

In the preceding example, we are using the foreach binding to repeatedly render a table row. The HTML markup within the tbody element is used as the template to render each contact. Using templates in this way is only useful with control structures, such as loops and conditions. It is not very useful if you plan to use the template in multiple different locations in your application. This is where named templates are handy.

Note

The foreach binding is the Knockout construct for looping over an array. We will explore foreach binding in more details later on.

Let's rewrite our previous example to use a named template:

<table>
  <thead>
    <tr>
      <th>Contact</th>
      <th>Phone Number</th>
    </tr>
  </thead>
  <tbody data-bind="template: {name: 'contact-template', foreach: contacts}">
  </tbody>
</table>

<script type="text/html" id="contact-template">
  <tr>
    <td data-bind="text: name"></td>
    <td data-bind="text: phoneNumber"></td>
  </tr>
</script>

In this example, we extracted the template into a script block and gave it an ID, contact-template. We then modified the data binding to add a binding for the template. The template binding takes a name of the template, which is the ID of the script block containing our template. The foreach binding is a parameter for the template binding.

Templates in Knockout are both flexible and powerful. You can dynamically choose a template by pointing the name attribute of the template to an observable in your model. You can also add a post processing logic to the template by adding the afterRender attribute. This attribute can point to a function in your view model that takes the HTML element as a parameter.

Knockout also supports third party templating engines such as jQuery.tmpl and Underscore. The examples in this book use native Knockout templates. Native templates are more than adequate for most use case.

 

Understanding the module pattern and its use with Knockout


In the previous section, we explored the key features of Knockout. We learned the basics of declarative data binding, automated UI refresh, dependency tracking, and templating. Knockout does a really good job of simplifying web application development by providing these features. However, it does not solve the problem of bringing structure to your JavaScript code.

Unlike an object-oriented programming language such as Java or C#, JavaScript does not enforce any particular structure. This is both a blessing and a curse. Blessing in the sense that you can bring your own rules on how to structure your code. This gives you power and flexibility. It can be a curse if you do not follow any structure as in that case, your code base becomes too large and complex. Giving structure to your JavaScript code becomes more and more important as you write complex JavaScript applications. Structuring your JavaScript will make the code more maintainable and readable. It also helps to make the code more testable.

The concept

An elegant yet simple way of giving structure to your JavaScript code is by using the module pattern. It is important to understand the basic concepts behind the module pattern as we will be using this pattern throughout this book. Let's get started with the basic concept.

Central to the module pattern is the concept of a module. A module is a component that encapsulates everything that is required to accomplish a set of related tasks. This includes data as well as behavior. Here is an example of creating a module using the module pattern in JavaScript:

(function () {
  /* module code */
}) ( );

Let's deconstruct and explore what the preceding code does. Since JavaScript does not provide a construct for creating modules or classes, we use the next best thing—the anonymous function construct. The preceding code constructs and executes an anonymous function. The module code inside this function maintains privacy from the outside world. This is because creating a function creates a new scope. The module code also maintains its state throughout the life cycle of the module. Notice the parenthesis ( ) at the end of our function. These parenthesis execute our anonymous function straight after creation and creates our module.

Note

By convention, the modules are named with uppercase first letter.

We need a way to namespace our newly created module. This will allow us to access any public attributes that the module might expose:

var Module = (function () {
  /* module code */
})( );

In the module we defined here, the scope of any attributes or function is confined to the module. You cannot access an attribute that is declared within the module. This is exactly what we want to do—encapsulate everything related to a set of tasks.

Public and private members

If everything is now encapsulated, how does the outside world interact with our module? The answer is a return object with references to attributes and functions that we want to expose to the outside world. With the public and private members defined, the module will look similar to this:

var Module = (function () {
  /* private attribute */
  var privateAttribute;

  /* private function */
  var privateFunction = function () {};

  /* public attribute */
  var publicAttribute;

  /* public function */
  var publicFunction = function () {};

  /* return object with reference to public attributes and functions */
  return {
    publicAttribute: publicAttribute,
    publicFunction: publicFunction
  };
})();

The scope of the private members, prefixed with the word private, is confined to the module. The public members, prefixed with the word public, are exposed to the outside world through the return object. The return object simply references the public members. We can now access the public members as follows:

Module.publicAttribute = 'foo';
Module.publicFunction();

Initializing the module

One final element I want to add to my module is a function that initializes the module. Some people like to call it a constructor. Strictly speaking, a constructor is a function that creates an object. It can, however, contain initialization logic. This is not what our function will do. Our function will only initialize the module, hence I won't be calling it a constructor. You can choose any name for your initialization function. I like to call it init. Let's add the init function to our module:

var Module = (function () {
  /* private attribute */
  var privateAttribute;
  /* private function */
  var privateFunction = function () {};

  /* public attribute */
  var publicAttribute;

  /* public function */
  var publicFunction = function () {};

  var init = function() {
    /* Module initialization logic*/
  };

  /* fire the init function */
  init();

  /* return object with reference to public attributes and functions */
  return {
    publicAttribute: publicAttribute,
    publicFunction: publicFunction
  };
})();

In the preceding example, we can see the init function being declared. The scope of this function is private as it is not exposed by the return object. Declaring the function does not mean that our function will execute when the module is created. We execute the function by calling it after declaration:

/* execute the init function */
init();

On most occasions, we want to execute our init function after the HTML is fully loaded by the browser and the DOM is ready. This is where jQuery comes handy. We can use a feature provided by jQuery to execute the init function once the HTML is fully loaded and the DOM is ready. This is done by replacing the call init(); with:

/* execute the init function once the DOM is ready */
jQuery(init);

Passing any function as an argument to the jQuery function executes it once the DOM is ready. In the preceding code, we pass the init function as an argument to the jQuery function.

Tip

The dollar sign, $, is a short hand for jQuery. jQuery() is the same as $().

Using the module with view model

Now that we have learned the basic concepts behind the module pattern, let's declare the contact view model we used earlier as a module:

var ContactViewModel = (function () {
  var contact = {
    id: ko.observable(1),
    name: ko.observable('John'),
    phoneNumber: ko.observable(00001111)
  };

  Var retrieveContact = function (){
    /* logic to retrieve contact form server side data repository */
  };

  Var updateContact = function (newPhoneNumber){
    /* logic to update the contact with new phone number */
  };

  var init = function() {
    /* Module initialization logic*/
  };

  /* execute the init function once the DOM is ready */
  $(init);

  return {
    contact: contact,
    updateContact: updateContact
  };
})();

Our preceding module is referenced by ContactViewModel. It has a contact model and functions to retrieve and update the contact. It also has an initialization function, which will be executed once the DOM is ready. The module exposes the contact model and the updateContact function as public members to the outside world. The retrieve contact function remains private to the module.

 

Building the address book application


Now that we have a basic understanding of the design patterns we will be using and the key features of Knockout, let's dive into building our first application. Our first application is an address book, which is used to store and display contact details of your family and friends. The application lets you add a contact's name and phone number. The contacts are displayed in a table. This is a simple application that highlights some of the basic features that Knockout has to offer.

We will take an iterative approach in building this and all the other example applications in this book. The idea behind an iterative approach is to build the application in small portions. Each portion will deliver a subset of the features. We will continue to evolve the application until the full application is implemented.

A word on the development environment

You can use any Integrated Development Environment (IDE) of your choice or simply use a text editor like notepad or vi to develop the application. I recommend using an IDE as it increases developer productivity by many folds. I use an open source IDE called eclipse. You can find out more about eclipse at http://eclipse.org/.

Web applications are typically hosted on a web server. You can choose a web server that you are familiar with to host the example applications in this book. The two web server that I recommend are:

  • Apache HTTP Server: This is the most popular web server on the internet. You can find out more about Apache at http://httpd.apache.org/.

  • Node.js HTTP Server: Node.js has gained popularity in recent times. Find out more about Node.js at http://nodejs.org/.

You do not require a web server for developing a pure client-side web application using only HTML, JavaScript, and CSS. You can simply view the HTML files by opening them in a browser from your file system. Most examples in this book do not require a web server for development unless you are planning to host the applications or the application requires a server-side component such as a RESTful API endpoint.

Downloading the libraries

First, we need to download the libraries that we require. The two libraries we require are Knockout and jQuery.

Download Knockout from the Knockout's website at http://knockoutjs.com/. This should be a single JavaScript file.

Next, download jQuery from the jQuery's website at http://jquery.com/. This should also be a single JavaScript file.

Creating the skeleton

First, we will create the skeleton for our address book application. We will use this skeleton for all the example applications in this book.

Note

A skeleton is a high-level structure that compiles but does not provide any application features. The skeleton is iteratively evolved into a working application. The skeleton forms a template that provides the basic structure, which can be then used in other applications.

Let's create the folder structure for development by following these steps:

  1. Create the AddressBook folder. This is the main folder that houses our address book application.

  2. Add a WebContent folder under the AddressBook folder. This folder holds the content that gets published to the web.

  3. Add a javascript folder under the WebContent folder. As the folder name suggests, this folder will contain all our JavaScript files.

Now that we have the folder structure in place, let's add some files to our folders by following these steps:

  1. Add the Knockout library that you downloaded to the javascript folder.

  2. Add the JQuery library that you downloaded to the javascript folder.

  3. Create the addressbook.js file under the javascript folder.

  4. Create the addressbook.html file under the WebContent folder.

Following these steps should result a folder structure that looks similar to this:

Now that we have created the folder structure, we can add code to our HTML and JavaScript files. Open the addressbook.html file and add the following HTML code:

<!DOCTYPE HTML>
<html>
  <head>
    <meta http-equiv="Content-Type" content="text/html" />
    <title><!-- add title --></title>
    <!-- the jquery library -->
    <script type="text/javascript" src="javascript/jquery-2.1.3.min.js"></script>
    <!-- the knockout library -->
    <script type="text/javascript" src="javascript/knockout-3.2.0.js"></script>
    <!-- module for our application -->
    <script type="text/javascript" src="javascript/addressbook.js"></script>
  </head>
  <body>
    <!-- add body content -->
  </body>
</html>

The file in its current state does not do much. It references Knockout and jQuery libraries from our javascript folder. It also references our addressbook.js application module.

Tip

Any application modules, such as addressbook.js, should always be referenced after the Knockout and jQuery libraries. This is because the application module will use the ko and $ objects defined by these libraries. Make sure that the application module is referenced after these libraries if you get an error, stating that either ko or $ is undefined.

Open the addressbook.js file and add the following code; this code defines our empty AddressBook module:

/* Module for Address Book application */
var AddressBook = function () {

  /* add members here */

  var init = function () {
    /* add code to initialize this module */
  };

  /* execute the init function when the DOM is ready */
  $(init);
  
  return {
    /* add members that will be exposed publicly */
  };
}();

View the addressbook.html file in your browser. The browser should give you a black page, which is not very exciting, but what we have done is created the skeleton for our application. Next, we will start building the application features.

Adding the application features

Our address book application captures, stores, and displays contacts details of our family and friends.

Capturing and storing contacts

Let's develop the functionality to capture and store the contacts. The two pieces of information we want to capture and store is the contact name and phone number. This is defined as a model in our AddressBook module. To do this, open the addressbook.js file and add the following code:

/* add members here */
  var contact = {
  name: ko.observable(),
  phoneNumber: ko.observable()
};

The code defines a contact object with two attributes—name and phoneNumber. The attributes are Knockout observables. We will bind the contact object to our HTML input fields to capture user input. Before we add the HTML fields and the binding construct, we need to expose the contact object publicly so that it can be accessed outside our module, for example, by our HTML binding construct. This is done by adding the contact object to the return statement of our module. Let's add the contact object to the return statement. Here is what the code should be:

return {
  /* add members that will be exposed publicly */
  contact: contact
};

Let's now add the HTML input fields to our view and bind them to our view model. Open addressbook.html and the following code in the body of the HTML; take this opportunity to also change the title of the HTML page to something more appropriate like Knockout: Address Book Example:

<p>Name <input type="text" data-bind="value: AddressBook.contact.name" /></p>

<p>Phone Number <input type="text" data-bind="value: AddressBook.contact.phoneNumber" /></p>

In the preceding code, we have declared two HTML input fields, one for the contact name and the other for the contact phone number. We also added the binding construct by using data-bind. Notice the way we accessed the model. For example, to access the name attribute of the contact, we used the name of our module, AddressBook; followed by the name of our model object, contact; followed by the name of the attribute, name.

The capturing of user input is not complete without a button to indicate that the user has entered a new contact. Add a button to your HTML by inserting the following line after the input text fields:

<p><button data-bind="click: AddressBook.addContact">Add</button></p>

The preceding code will add a button to your view with the Add label. It also adds a click binding. As a result of the click binding, an addContact function will get executed when the user clicks on the Add button. We have not yet defined the addContact method. Let's do this by adding the following code to our view model:

var addContact = function () {
console.log("Adding new contact with name: " + contact.name() +" and phone number: " + contact.phoneNumber());
};

The code displays the values of contact name and phone number from the contact object in the browser console. Notice how we access the value of the name and phoneNumber observables. The addContact method needs to be publicly accessible as it is referenced in our view. Let's do this by adding it to the return statement of our module. Our return statement should now look similar to this:

return {
  /* add members that will be exposed publicly */
  contact: contact,
  addContact: addContact
};

We are missing one very important step before we can run what we have developed so far. That step is to activate Knockout. Add the following line of code to the init function in our module:

var init = function () {
  /* add code to initialize this module */
  ko.applyBindings(AddressBook);
};

Here, the applyBindings function takes view model as the parameter and applies the bindings declared in our view to the model and behavior, defined in our view model. We pass the view model to the applyBindings function by passing our AddressBook module.

Now that our application is capturing the contact details, let's develop the functionality to store the contacts. The contacts will be stored using an array. We cannot use the normal JavaScript array as we will need to bind the array to our view in order to display the contacts. Knockout provides a way to construct an array of observables. To make an array of contacts, add the following code to our AddressModule below the contact:

var contacts = ko.observableArray();

The observableArray function returns an object, which can track the objects it holds. This means that any subscriber will be notified when an object is added or removed from it.

Note

The members of the objects that observableArray hold, do not become observables. This, however, can be achieved through additional code.

Now that we have declared our contacts array, let's add the contact to our contacts array. Modify the addContact function and add the following line of code to push a contact to the contacts array. Your addContact function should look similar to this:

var addContact = function () {
console.log("Adding new contact with name: " + contact.name() +" and phone number: " + contact.phoneNumber());

//add the contact to the contacts array
contacts.push({name: contact.name(), phoneNumber: contact.phoneNumber()});
};

Knockout observableArray provides useful methods to interact with the array. We have used a method push, which insets a new item at the end of the array. We pass a new contact object to the push method by creating an object with name and phone number as attributes. The value of the attributes come from our name and phoneNumber observables in the contact object.

You may have noticed that the input fields for name and phone numbers retain their values after the add button is clicked and the object is added to the contacts array. This is not for user experience as the user has to clear the inputs before a new contact can be added. To clear the input fields, add the following method to your AddressBook module:

var clearContact = function () {
  contact.name(null);
  contact.phoneNumber(null);
};

Call this method from your addContact method after pushing the new contact to the contacts array. The clearContact method clears the values of the name and phoneNumber observable by setting them to null. The two-way data binding takes care of updating the HTML input fields. You do not have to add the clearContact method to the return statement of the module as this a private member, which is not required by any other external module or view.

So far, we have:

  • Developed our application skeleton

  • Created our view with two HTML input text fields for capturing contact name and phone number, and a button to allow user to add a contact

  • Created our module with a model for capturing the user input and storing contacts in an array

  • Added functionality to add the contact to the contacts array and clear the input fields

  • Added declarative binding to our view to bind the HTML input fields to the contact object and the Add button to the addContact function in our view model

  • Activated Knockout by calling the applyBindings function in our init function

Let's run our application and see what happens. Open addressbook.html in a browser. Don't forget to open the console window of the browser. Try adding a contact.

Note

You can open the console window in Chrome by hitting the F12 key and selecting Console in the menu bar.

The application should look similar to this:

Displaying contacts

The next application feature we will add to our address book application is displaying the list of contacts in a table. We will use the HTML table element with foreach binding. Let's get straight into it.

Open the addressbook.html file and add the following code under the input HTML fields:

<table>
  <thead>
    <tr>
      <th>Name</th>
      <th>Phone Number</th>
    </tr>
  </thead>
  <tbody data-bind="foreach: AddressBook.contacts">
    <tr>
      <td data-bind="text: name"></td>
      <td data-bind="text: phoneNumber"></td>
    </tr>
  </tbody>
</table>

In the preceding code, we are using the foreach binding to repeatedly render a table row of contacts. The foreach binding provides a loop construct to display HTML elements based on a template. The HTML markup within the tbody element is used as the template to render each contact.

The data binding declaration in the preceding code refers to the contacts array in our module. We have not yet declared the contacts array to be publicly accessible. Make the contacts array publicly accessible by adding it to the return statement of your module. The return statement of AddressBook module should look similar to this:

return {
  /* add members that will be exposed publicly */
  contact: contact,
  contacts: contacts,
  addContact: addContact
};

Tip

Downloading the example code

You can download the example code files from your account at http://www.packtpub.com for all the Packt Publishing books you have purchased. If you purchased this book elsewhere, you can visit http://www.packtpub.com/support and register to have the files e-mailed directly to you.

Run the application by opening addressbook.html in a browser. Try adding some contacts. You should now be able to see the newly added contacts displayed in the table. The application should look similar to this:

Adding style to your application with Bootstrap

We have added the application features to our address book application, but the application does not look very visually appealing. Let's make it a bit more attractive by adding Bootstrap to our address book application.

Note

Bootstrap is a popular HTML, CSS, and JavaScript framework for developing web applications. It provides out-of-the-box styles for HTML elements such as labels, buttons, and tables. Find out more about Bootstrap at http://getbootstrap.com/.

Follow these steps to download and set up Bootstrap:

  1. Download Bootstrap from the Bootstrap website.

  2. Create a bootstrap folder under WebContent.

  3. Extract the contents of the download package in the bootstrap folder created in the previous step.

Your folder structure should look similar to this:

You are now ready to use Bootstrap. Include the Bootstrap theme in your application by adding the following line to your HTML inside the head element. Your head element should look similar to this:

<head>
  <meta http-equiv="Content-Type" content="text/html" />
  <title>Knockout : Address Book Example</title>
  <link rel="stylesheet" href="bootstrap/css/bootstrap.min.css">
  <script type="text/javascript" src="javascript/jquery-2.1.3.min.js"></script>
  <script type="text/javascript" src="javascript/knockout-3.2.0.js"></script>
  <script type="text/javascript" src="javascript/addressbook.js"></script>
</head>

You are free to make your own layout and style choices if you are familiar with Bootstrap. If not, you can follow these steps and make the changes to add the Bootstrap styling to your application:

  1. Wrap the contents of the HTML body in a div element and give it a class, container.

  2. Add a heading using the h1 element just after the body and wrap it in a div element. Give the div element a page-header class like this:

    <div class="page-header"> <h1>My Address Book</h1></div>
  3. Wrap the HTML input fields and the button in a p element. Remove any p elements that the input fields were previously wrapped in.

  4. Add btn and btn-primary classes to the Add button, like this:

    <button class="btn btn-primary" data-bind="click: AddressBook.addContact">Add</button>
  5. Add the table class to the contacts table, like this:

    <table class="table">

After making these modifications, your address book application should look, like this:

You have successfully completed your first Knockout application! Let's look at some useful resources and summarize what we learned.

 

Resources


Knockout is a popular open source JavaScript library supported by a vibrant community. The following is a list of some key resources to help you with your journey:

 

Summary


In the first half of this chapter, we covered some basic concepts and patterns that helped us understand how Knockout works. After a brief overview of Knockout, we dived into the MVVM pattern. We explored the concept behind this pattern and saw how it helps in reducing complexities of web application development. We then explored the key features of Knockout that included declarative bindings, automatic UI refresh, dependency tracking, and templating. We looked at the module pattern and learned how we can use it to give structure to our Knockout application.

In the second half of this chapter, we built our first Knockout application. The application was an address book, which was used to store and display contact details of your family and friends. The application let you add a contact name and phone number. The contacts were displayed in a table.

This chapter provided the necessary concepts, pattern, and skeleton code to start developing more complex applications, which follow in the next chapters.

About the Author
  • Adnan Jaswal

    Adnan Jaswal is technologist with vast knowledge and experience in technology consultancy, solution architecture, and software development. He has designed and developed software for government, education, financial, cyber security, logistics, and aviation industries. He believes in the digital revolution and the power it possesses to change the way people and businesses interact with technology. He is passionate about JavaScript technologies and views them as an enabler of digital change.

    He has worked for companies such as CA Technologies and Object Consulting. He currently works, as a manager, for one of the big four professional services networks. His role involves technology consulting, architecting, leading teams, developing software, and helping clients respond to digital disruption.

    He lives in Melbourne, Australia, with his wife and two children. He can be found on LinkedIn at https://www.linkedin.com/in/adnanjaswal.

    Browse publications by this author
Latest Reviews (1 reviews total)
KnockoutJS by Example
Unlock this book and the full library FREE for 7 days
Start now