What is Knockout

A JavaScript library that helps you create rich, maintainable, and responsive user interfaces. Changes made to observed objects are reflected back to other objects that depend on them. The declarative bindings help to eliminate jQuery spaghetti!

Some key features

What is MVVM

The Model View ViewModel pattern is used to facilitate a clear separation of the graphical user interface (ie. markup) from the development of the business logic or back end logic known as the data model.

Model: a data access layer that contains the content

View: the markup necessary to display content, controls, etc

View model: an abstraction of the view that serves in mediating between the view and the model. Exposes public properties and commands. Passes commands from the view to the model.

Read the Wikipedia article for more information! Too much to cover here.

Kinda? Similar Libraries

Frameworks

  • Ember (MVC)
  • Angular (MVC)
  • Batman (MVC)

Templating (View only)

  • Handlebars
  • ReactJS HOT!
  • Rivets
  • Backbone Views

Observables

var personModel = {
  name: ko.observable('Adam'),      // Initialized with a value
  favouriteDrink: ko.observable(),
};

// Get value
personModel.name();

// Set value
personModel.favouriteDrink('Beer');

Observable Arrays

// Define our viewModel
var viewModel = {
  // Create our fruits property with two fruits pre-defined
  fruits: ko.observableArray(["Apples", "Pears"]);
};

// Append another fruit to fruits
viewModel.fruits.push("Bananas");

// Give me some Apples
viewModel.fruits.shift();

Declarative Bindings

A simple and obvious way to connect parts of your UI to your data model.

<span data-bind="text: firstName"></span>

<input data-bind="value: firstName">

<select data-bind="options: availableCountries, value: birthCountry"></select>

<div data-bind="visible: hasName">
  I am visible when hasName evaluates true
</div>

<span data-bind="css: { inStock: stockLevel() > 0 }"></span>

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

...and many more!

Two-Way Binding

Automatic synchronization of data between the model and view components

var viewModel = {
  firstName: ko.observable("Adam"),
};
ko.applyBindings(viewModel);
<input data-bind="value: firstName, valueUpdate: 'keyup'">
<span data-bind="text: firstName"></span>

Bindings without Containers

Intialized by <!-- ko binding: argument --> and closed by <!-- /ko -->

Useful if you don't need a parent HTML element

<!-- ko foreach: pages -->
  <!-- ko if: $root.page() == $data -->
  <em class="current" data-bind="text: $data"></em>
  <!-- /ko -->

  <!-- ko ifnot: $root.page() == $data -->
  <a data-bind="text: $data, click: $root.page"></a>
  <!-- /ko -->
<!-- /ko -->

Computed Observables

Functions that automatically update when their dependencies change.

this.fullName = ko.computed(function() {
  return this.firstName() + " " + this.lastName();
}, this);

Some of my use cases

Updating window location in Single Page Apps
ko.computed(function() {
  var params = { page: this.currentPage() };    // Our dependency(s)
  uri.fragment(params);                         // uri is provided by URI.js
  window.location = uri.toString();
}, this);
Automatically fetching search result JSON data
ko.computed(function() {
  $.getJSON(window.location.href, { category: this.category() });
}, this).extend({ throttle: 500 });

Use Cases

Search Form

function viewModel() {
    this.movies = ["Back to the Future", "Ghostbusters", "Caddyshack", "The Phantom"];

    this.filter = ko.observable(""),

    this.filteredMovies = ko.computed(function () {
        var filter = this.filter().toLowerCase();

        if (!filter) {
            return this.movies;
        } else {
            return ko.utils.arrayFilter(this.movies, function (movie) {
                return movie.toLowerCase().indexOf(filter) !== -1;
            });
        }
    }, this);
};
ko.applyBindings(new viewModel());
<input data-bind="value: filter, valueUpdate: 'keyup'">
<ul data-bind="foreach: filteredMovies">
  <li data-bind="text: $data"></li>
<ul>

Search Form Demo

function viewModel() {
    this.movies = ["Back to the Future", "Ghostbusters", "Caddyshack", "The Phantom"];

    this.filter = ko.observable(""),

    this.filteredMovies = ko.computed(function () {
        var filter = this.filter().toLowerCase();

        if (!filter) {
            return this.movies;
        } else {
            return ko.utils.arrayFilter(this.movies, function (movie) {
                return movie.toLowerCase().indexOf(filter) !== -1;
            });
        }
    }, this);
};
ko.applyBindings(new viewModel());
<input data-bind="value: filter, valueUpdate: 'keyup'">
<ul data-bind="foreach: filteredMovies">
  <li data-bind="text: $data"></li>
<ul>

Todo List

function Task(title) {
  this.title = ko.observable(title);
  this.completed = ko.observable(false);

  this.toggleCompleted = function() {
    this.completed(! this.completed());
  };
};

function ViewModel() {
  this.tasks = ko.observableArray();
  this.currentTask = ko.observable();

  this.addTask = function() {
    this.tasks.push(new Task(this.currentTask()))
    this.currentTask("");
  }.bind(this);

  this.tasks.push(new Task("Drink beer"));
  this.tasks.push(new Task("Write code"));
};
ko.applyBindings(new ViewModel());

<ul data-bind="foreach: tasks">
  <li data-bind="text: title, click: toggleCompleted, css: { completed: completed }"></li>
</ul>

<form data-bind="submit: addTask">
  <input data-bind="value: currentTask">
</form>
      

Todo List Demo


ul {
  list-style: none;
  margin: 0;
  padding: 0;
}

li {
  cursor: pointer;
  margin: 0.5em 0.5em 0.5em 0;
}

.completed {
  text-decoration: line-through;
  opacity: 0.8;
}
      
function Task(title) {
  this.title = ko.observable(title);
  this.completed = ko.observable(false);

  this.toggleCompleted = function() {
    this.completed(! this.completed());
  };
};

function ViewModel() {
  this.tasks = ko.observableArray();
  this.currentTask = ko.observable();

  this.addTask = function() {
    this.tasks.push(new Task(this.currentTask()))
    this.currentTask("");
  }.bind(this);

  this.tasks.push(new Task("Drink beer"));
  this.tasks.push(new Task("Write code"));
};
ko.applyBindings(new ViewModel());

<ul data-bind="foreach: tasks">
  <li data-bind="text: title, click: toggleCompleted, css: { completed: completed }"></li>
</ul>

<form data-bind="submit: addTask">
  <input data-bind="value: currentTask">
</form>
      

More Information

KnockoutJS
knockoutjs.com
MVVM
Model View ViewModel (Wikipedia)

Thank You

Github
github.com/adam12
Twitter
@adamrdaniels
Sourcecode
github.com/adam12/knockoutjs-devtricks-2014

/