Search icon CANCEL
Subscription
0
Cart icon
Your Cart (0 item)
Close icon
You have no products in your basket yet
Save more on your purchases now! discount-offer-chevron-icon
Savings automatically calculated. No voucher code required.
Arrow left icon
Explore Products
Best Sellers
New Releases
Books
Videos
Audiobooks
Learning Hub
Conferences
Free Learning
Arrow right icon
Arrow up icon
GO TO TOP
MASTERING KNOCKOUTJS

You're reading from   MASTERING KNOCKOUTJS Use and extend Knockout to deliver feature-rich, modern web applications

Arrow left icon
Product type Paperback
Published in Nov 2014
Publisher
ISBN-13 9781783981007
Length 270 pages
Edition 1st Edition
Arrow right icon
Author (1):
Arrow left icon
Timothy Moran Timothy Moran
Author Profile Icon Timothy Moran
Timothy Moran
Arrow right icon
View More author details
Toc

Table of Contents (11) Chapters Close

Preface 1. Knockout Essentials FREE CHAPTER 2. Extending Knockout with Custom Binding Handlers 3. Extending Knockout with Preprocessors and Providers 4. Application Development with Components and Modules 5. Durandal – the Knockout Framework 6. Advanced Durandal 7. Best Practices 8. Plugins and Other Knockout Libraries 9. Under the Hood Index

Control flow bindings

So far, we have looked at one-way and two-way bindings that set or sync data with an attribute on an HTML element. There is a different kind of binding that Knockout uses for modifying the DOM by adding or removing nodes. These are the control flow bindings, and they include foreach, if, with, and template.

All of the control flow bindings work by actually removing their content from the DOM and creating an in-memory template from it. This template is used to add and remove the content as necessary.

Control flow bindings (except if) also introduce a binding context hierarchy. Your root binding context is the viewmodel passed to ko.applyBindings. The data-bind attributes have access to properties in the current context. Control flow bindings (other than if) create a child-binding context, meaning that data-bind attributes inside the control flow binding's template have access to the properties of their context and not the root context. Bindings inside a child context have access to special properties to allow them to navigate the context hierarchy. The most commonly used are:

  • $parent: This accesses the binding context of the immediate parent. In this example, group and $parent.group refer to the same property because $parent accesses the context outside of the person:
    <span data-bind="text: group"></span>
    <div data-bind="with: person">
      <span data-bind="text: name"></span>
    <span data-bind="text: $parent.group"></span>
      </div>
  • $parents[n]: This is an array of parent contexts. The $parents[0] array is same as $parent.
  • $root: This is the root viewmodel, the highest context in the hierarchy.
  • $data: This is the current viewmodel, useful inside foreach loops.

    Note

    For a complete list of context properties, see the Knockout documentation for them at http://knockoutjs.com/documentation/binding-context.html.

The if binding

The if binding takes a value or expression to evaluate and only renders the contained template when the value or expression is truthy (in the JavaScript sense). If the expression is falsy, the template is removed from the DOM. When the expression becomes true, the template is recreated and any contained data-bind attributes are reapplied. The if binding does not create a new binding context:

<div data-bind="if: isAdmin">
  <span data-bind="text: user.username"></span>
  <button data-bind="click: deleteUser">Delete</button>
</div>

This div would be empty when isAdmin is false or null. If the value of isAdmin is updated, the binding will re-evaluate and add or remove the template as necessary.

There is also an ifnot binding that just inverts the expression. It's useful if you want to still use a property reference without needing to add a bang and parentheses. The following two lines are equivalent:

<div data-bind="if: !isAdmin()" >
<div data-bind="ifnot: isAdmin">

The parentheses are needed in the first example because it is an expression, not a property name. They are not needed in the second example because it is a simple property reference.

The with binding

The with binding creates a new binding context using the supplied value, which causes bindings inside the bound element to be scoped to the new context. These two snippets are functionally similar:

<div>
  First Name:
<span data-bind="text: selectedPerson().firstName"></span>
  Last Name:
<span data-bind="text: selectedPerson().lastName"></span>
</div>

<div data-bind="with: selectedPerson">
  First Name:
<span data-bind="text: firstName"></span>
  Last Name:
<span data-bind="text: lastName"></span>
</div>

While saving a few keystrokes and keeping your bindings easier to read is nice, the real benefit of the with binding is that it is an implicit if binding. If the value is null or undefined, the content of the HTML element will be removed from the DOM. In the cases where this is possible, it saves you from the need to make null checks for each descendant binding.

The foreach binding

The foreach binding creates an implicit template using the contents of the HTML element and repeats that template for every element in the array.

This viewmodel contains a list of people we need to render:

var viewmodel = {
  people: [{name: 'Tim'}, {name: 'Justin}, {name: 'Mark'}]
}

With this binding, we create an implicit template for the li element:

<ul data-bind="foreach: people">
  <li data-bind="text: name"></li>
</ul>

This binding produces the following HTML:

<ul>
  <li>Tim</li>
  <li>Justin</li>
  <li>Mark</li>
</ul>

The thing to note here is that the li element is binding against name, which is the property of a person. Inside the foreach binding, the binding context is the child element. If you need to refer to the child itself, you can either use $data or supply an alias to the foreach binding.

The $data option is useful when the array only contains primitives that you want to bind against:

var viewmodel = {
  people: ['Tim', 'Justin, 'Mark']
}
...
<ul data-bind="foreach: people">
  <li data-bind="text: $data"></li>
</ul>

The alias option can clean up your code, but it is particularly useful when you have a nested context and want to refer to the parent. Refer to the following code:

<ul data-bind="foreach: { data: categories, as: 'category' }">
    <li>
        <ul data-bind="foreach: { data: items, as: 'item' }">
          <li>
            <span data-bind="text: category.name"></span>:
            <span data-bind="text: item"></span>
          </li>
         </ul>
    </li>
</ul>

This can be achieved with $parent, of course, but it is more legible when using an alias.

You have been reading a chapter from
MASTERING KNOCKOUTJS
Published in: Nov 2014
Publisher:
ISBN-13: 9781783981007
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