Book Image

KnockoutJS by Example

By : Adnan Jaswal
Book Image

KnockoutJS by Example

By: Adnan Jaswal

Overview of 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.
Table of Contents (17 chapters)
KnockoutJS by Example
Credits
About the Author
Acknowledgment
About the Reviewers
www.PacktPub.com
Preface
Index

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.