Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
KNOCKOUTJS BLUEPRINTS

You're reading from   KNOCKOUTJS BLUEPRINTS Learn how to design and create amazing web applications using KnockoutJS

Arrow left icon
Product type Paperback
Published in Feb 2015
Publisher
ISBN-13 9781783980840
Length 218 pages
Edition 1st Edition
Languages
Arrow right icon
Author (1):
Arrow left icon
Carlo Russo Carlo Russo
Author Profile Icon Carlo Russo
Carlo Russo
Arrow right icon
View More author details
Toc

Table of Contents (7) Chapters Close

The magic of KnockoutJS unveiled

We saw that all the magic of KnockoutJS starts with the call to:

ko.applyBindings(myViewModel);

This function gets two parameters: a View Model and a DOM element. You can skip the second parameter and it will default to the document.body.

First of all, it takes the View Model, and makes a ko.bindingContext from the View Model.

BindingContext tracks all the following information:

  • $parent: This is the View Model of the parent context; for example, every binding inside a foreach binding will have the foreach view model as $parent
  • $parents: This refers to an array with all the parents context; empty for the root View Model. You can use an indexer to traverse the hierarchy (for deep-nesting); for instance, $parents[1] will get you the 2nd ancestor and so on
  • $root: This is the View Model of the highest parent; itself for the root view model.
  • $rawData: This is the original View Model, before unwrapping (to understand "unwrapping" better, imagine that you have a property, x = ko.observable(12), and you execute x(); you are unwrapping the observable to get the value 12)
  • $data: This refers to the unwrapped View Model.

Then, it starts to apply the bindings to the node:

  • It stores the bindingContext inside the node data (but only if the current context is different from the context inside the parent node)
  • It checks if the current node contains the data-bind attribute, and applies the binding to each of them
  • For each binding, it executes the init function inside a call to ko.dependencyDetection.ignore, and then the update function inside a call to ko.dependentObservable; in this way, the update function of each binding handler works as a computed observable (more about computed observables a little later)
  • It executes these steps recursively for each descendant

    Note

    Binding to the same node more than once is not permitted; when you call ko.applyBindings it checks if the node is already bound and it will throw an exception.

    When you think you need to apply the binding again (maybe you changed the DOM structure without KnockoutJS) to the same node, the best idea is to rethink why you should do it; often you will see you can use the with binding handler to solve this problem in a KnockoutJS way.

    Or, if you are absolutely sure this is the best solution, you can use ko.cleanNode to reset the element to its previously unbound state.

The change of the bindingContext is done inside a few binding handlers (with, foreach, and so on) because they create a child bindingContext; you can do the same inside your custom binding handler's init function (for more information visit this URL: http://knockoutjs.com/documentation/custom-bindings-controlling-descendant-bindings.html).

Note

Before looking at a practical example, let's understand what a computed observable is.

ko.computed is the third kind of Observable KnockoutJS supports; it's defined by a function, and each time it runs it registers itself as subscriber of any Observable found during the evaluation.

This is the same method KnockoutJS uses for the binding handler you find in the View.

In a few words, a computed observable is an observer of another observer; the easiest example is the full name computed observable, defined as the concatenation of the observable, name, and the observable, last name:

var firstName = ko.observable("Bob"), 
    lastName = ko.observable("Smith");
var fullName = ko.computed(function() {
    return firstName() + " " + lastName(); 
});

The property fullName here gets evaluated each time one of its internal observables changes.

Let's understand step by step what happens when you execute ko.bindingHandler(viewModel) in the current document.

We start with the following DOM structure:

The magic of KnockoutJS unveiled

As the first step, it takes the document.body node to work on, as you can see in the following picture:

The magic of KnockoutJS unveiled

It creates and adds to the data of the node, body, a new BindingContext like this:

ko.bindingContext: {
   $root: obj,
   $rawData: obj,
   $parents: [],
   $data: obj
}

Here obj is the parameter, viewModel.

Then it walks inside the descendants searching for the data-bind attribute; the next node to work on is the following one:

The magic of KnockoutJS unveiled

Here it finds a foreach binding, so it executes the binding handler; the init function returns controlsDescendantBindings, so it stops descending.

The function init of foreach saves the descendants and clears the DOM structure, so now we have this structure:

The magic of KnockoutJS unveiled

After this step it ends, because all the descendants of document.body are bound to our view model.

When the code updates viewModel.jewels with the content of the category list, the flow continues.

lock icon The rest of the chapter is locked
Register for a free Packt account to unlock a world of extra content!
A free Packt account unlocks extra newsletters, articles, discounted offers, and much more. Start advancing your knowledge today.
Unlock this book and the full library FREE for 7 days
Get unlimited access to 7000+ expert-authored eBooks and videos courses covering every tech area you can think of
Renews at $19.99/month. Cancel anytime
Banner background image