Wednesday, September 18, 2013

Knockout.js - Observables and Computeds

Yesterday I presented on a meetup about Knockout which is an MVVM library written in Javascript. I'll try to write my posts about this library based on the feedbacks I got after the presentation, hopefully making everything clear to the reader.



With Knockout it's very easy to totally separate the UI (the views) and the presentation logic (the view models). View models are actually models of the views. For example if you have a Boolean variable in your view model called visible, which probably encodes whether some part of the UI is visible if its value is true, BUT it says nothing about the state transition, namely when the value changes from true to false or vice versa. The simplest case is obviously when the referred part of the UI just disappears but it could slide up or fade out as well. These things are coded in the view via data-bindings. The view models are introducing a new abstraction layer, where the small details, like how things disappear really do not matter. Knockout does not deal with the model, the communication and modification can be implemented in the way you like. (And it's not necessarily stored in some remote DB, for example it can be the LocalStorage as well.)

I created a repo called knockout-playground on GitHub, I put some example code there, and will put the new examples there later, so you can play around with them easily. In this post I cover the first example and will write about the remaining examples in the subsequent posts.

The frame of my examples in the simple_examples folder is the following:
<html>
    <head>
        <script src="http://code.jquery.com/jquery-1.10.1.min.js">
        </script>
        <script src="http://ajax.aspnetcdn.com/ajax/knockout/knockout-2.2.1.js">
        </script>
        <script>
            $(document).ready(function() {
            });
        </script>
    </head>
    <body>
    </body>
</html>
When I'm talk about the Javascript part, imagine it in the callback above, and when I'm talking about the HTML and data-binding, then that's of course in the body of this frame.

In Knockout you can find everything in the object called ko. Any object with properties can be a view model. The root view model will be the one which is passed to the applyBindings function as a parameter:
var vm = {
  testVar1: "This is a string",
  testVar2: 42
};
ko.applyBindings(vm);
After this you can bind things in your HTML to the properties of the view model:
<div data-bind="text: testVar1"></div>
<input data-bind="value: testVar1" />
<div data-bind="text: testVar2"></div>
<input data-bind="value: testVar2" />

In the example above I used text and value binding. The text binding puts the binded variables value to into the element as text, the value binding sets it as the value attribute. One of the great thing about Knockout is that there are 2-way bindings if you use observables. This means if you change something on one of the views, it changes the values in the view model behind it and every view will be notified about the change. Thus it implements the observer pattern this way. After you put your values into observables in your view model and the user changes something in the input fields (and presses enter) the other parts of the view binded to that variable, will be updated automatically:
var vm = {
  testVar1: ko.observable("This is a string"),
  testVar2: ko.observable(42)
};
ko.applyBindings(vm);
As you can see, the creator function of observables is on the ko object as well. You can pass a parameter to it which will be the initial value of the observable. This creator function returns a function - that is your observable - and the value of the observable is hidden inside the function scope of the observable. If you want to fetch the value, you have to call your observable without parameters, and if you want to set the value, you have to pass a parameter to this function.

In this example, if you type something into the input fields, you have to press enter to update the value of the observable. Sometimes you might want it to be updated after each keydown. You can do it in this way:
<input data-bind="value: testVar1, valueUpdate: 'afterkeydown'" />
There are lot of cases when you have to create some new values based on one or more other variables (observables). In Knockout you can do it via computeds, these are actually observables and their value is computed by a callback specified by the programmer. It invokes the given callback function when you initialize the computed and every time when a referred observable's value is changed. It stores the computed value in it's function scope, so when you fetch it, it just returns this value.
function testVM() {
  var firstName = ko.observable("");
  var lastName = ko.observable("");
  var fullName = ko.computed(function() {
    return firstName() + " " + lastName();
  });
  return {
    firstName: firstName,
    lastName: lastName,
    fullName: fullName
  };
}
ko.applyBindings(testVM());
Here, the fullName is computed concatenating the other two observables' value with a space between them. From now on, you can bind this on your view as well:
<div data-bind="text: firstName"></div>
<div data-bind="text: lastName"></div>
<div data-bind="text: fullName"></div>
<input data-bind="value: firstName" />
<input data-bind="value: lastName, valueUpdate: 'afterkeydown'" />
You can find this example on GitHub. Feel free to play around, and drop me a mail or comment if you have questions. In the next post I will write about the second example, that's about observable arrays.

No comments:

Post a Comment

Share It