Download Source Files
View Live Demo
One of the main features of Knockout is, it can track each
element of UI and can update automatically. We will use this feature to bind
our metro tiles with its edit panel and when user will change in edit panel,
tiles of that panel will change automatically. Knockout will track edit panel
with its correspondent tile. For this, first we have to take a javascript
object where all the property of a metro tile is defined. Here we have title,
description and color as an observable. This observable will detect depedencies
between each tile and its edit panel and it will keep each tile object updated
automatically. Here is a how Tile object looks:
var Tile = function (title, description, color) {
var self = this;
self.title = ko.observable(title);
self.description = ko.observable(description);
self.tileColor = ko.observable(color);
self.editPanel = ko.observable(false);
self.expandEdit = function () {
self.editPanel(true);
};
self.collapseEdit = function () {
self.editPanel(false);
};
View Live Demo
Introduction
Knockout is a JavaScript library which dynamically binds
data between different html contents and follow Model-View-View-Model (MVVM)
design pattern. It’s one of the core
feature is Observable, which can detect changes in the view model and updates
the model according to that change. It’s a special JavaScript object that can
detect dependencies among controls automatically. In this article we will
mostly use observable to bind controls and update data dynamically.
Problem
Consider a metro tile where each tile has a title and
description. It has a background color as well. There can be multiple tiles of
same type. Our target is to create tiles dynamically and with every tile there
would be an edit panel where user can edit its title, description and
background color. If you are confused about this scenario then I recommend you
to view demo first.
Solution
var Tile = function (title, description, color) {
var self = this;
self.title = ko.observable(title);
self.description = ko.observable(description);
self.tileColor = ko.observable(color);
self.editPanel = ko.observable(false);
self.expandEdit = function () {
self.editPanel(true);
};
self.collapseEdit = function () {
self.editPanel(false);
};
};
Another additional observable is taken named editPanel which
will detect visibility of its tile’s edit panel. expandEdit and collapsEdit
will make editPanel true or false on click event. Here ObservableArray is taken
to store all tiles. addTile and removeTile functions will add new tile on the
view and remove tile will delete its corresponding tile from the view and from
the observableArray as well.
var TileModel = function () {
var self = this;
self.lines = ko.observableArray([
self.lines = ko.observableArray([
new Tile("Batman", "The Dark Night Rises", "#525252"),
new Tile("Transformer", "Revenge of the Fallen", "#DA532C")
]);
self.addTile = function () {
self.lines.push(new Tile("Title", "Description", RandomColor()))
};
self.removeTile = function (line) {
self.addTile = function () {
self.lines.push(new Tile("Title", "Description", RandomColor()))
};
self.removeTile = function (line) {
self.lines.remove(line) };
};
};
ko.applyBindings(new TileModel());
Also two tiles are pushed into the array on load and
observable will bind them with the HTML control automatically. Then finally
we’ve bind the full model with Knockout with ko.applyBindings().
Now let’s take a look at HTML controls which are bind with
observable object:
<a id="aCreate" class="btn-add-tile" data-bind='click: addTile'>Add Tile</a>
<div class="tile-container" data-bind='foreach: lines'>
<div class="tile-container" data-bind='foreach: lines'>
<div class="tile-area">
<div class="tile-elem" data-bind="style: { background: tileColor }">
<p class="tile-description" data-bind="text: description"></p>
</div>
<span class="tile-title" data-bind="text: title" style=""></span>
<div class="bottom-bar">
<span class="arrow" data-bind='click: expandEdit, ifnot:editPanel'>
<img src="Styles/images/MetroDown.png" alt="Open edit panel" /></span>
<span class="arrow" data-bind='click: collapseEdit, if:editPanel'>
<img src="Styles/images/MetroUp.png" alt="Close edit panel" /></span>
<span class="close-btn" data-bind='click: $parent.removeTile'>
<img src="Styles/images/MetroClose.png" alt="Remove tile" /></span>
</div>
<div data-bind="if: editPanel">
<div class="edit-panel">
<label>Title:</label>
<input class="text-title" data-bind="value: title, valueUpdate: 'afterkeydown'" maxlength="12" />
<label>Description:</label>
<textarea class="txtdesc" rows="2" data-bind="value: description, valueUpdate: 'afterkeydown'"></textarea>
</div>
</div>
</div>
<div class="tile-elem" data-bind="style: { background: tileColor }">
<p class="tile-description" data-bind="text: description"></p>
</div>
<span class="tile-title" data-bind="text: title" style=""></span>
<div class="bottom-bar">
<span class="arrow" data-bind='click: expandEdit, ifnot:editPanel'>
<img src="Styles/images/MetroDown.png" alt="Open edit panel" /></span>
<span class="arrow" data-bind='click: collapseEdit, if:editPanel'>
<img src="Styles/images/MetroUp.png" alt="Close edit panel" /></span>
<span class="close-btn" data-bind='click: $parent.removeTile'>
<img src="Styles/images/MetroClose.png" alt="Remove tile" /></span>
</div>
<div data-bind="if: editPanel">
<div class="edit-panel">
<label>Title:</label>
<input class="text-title" data-bind="value: title, valueUpdate: 'afterkeydown'" maxlength="12" />
<label>Description:</label>
<textarea class="txtdesc" rows="2" data-bind="value: description, valueUpdate: 'afterkeydown'"></textarea>
</div>
</div>
</div>
</div>
Here Knockout will search for data-bind attribute to bind
data with its model. Now on clicking Add Tile button we are calling addTile
function from TileModel which adds a new object to lines. With foreach,
Knockout is generating all the tile object stored in lines with a loop. Every
time a tile is added or removed, it tracks the changes in the model and bind
tiles dynamically. Now edit panel’s <textarea> is bind with <p> of
tile element with description object. <p> is updated when
<textarea> has an input character. We are updating the description object
after key down. Title will update same way like description. So the main thing
is all the observables track dependencies among the HTML controls and it updates
automatically.
Why Knockout?
Situation like this, where we have to bind data dynamically
and need to track all the dependencies among controls, then Knockout is best
choice. We will bind observable with UI controls and observable will notify
about changes and it will update automatically. Another good feature is, it can
update data very frequently, like when we bind afterkeydown method, observable
detects change and update correspondent element at that moment.
Run Source Code
Download the zip file, extract it, open KnockoutTiles.sln
file and hit F5. It’s a Asp.Net web project written in Visual Studio 2012 and
used knockout.js version 2.2.0.
Conclusion
Knockout follows MVVM pattern where observable track dependencies
between model and view model and update view automatically. This article gives
a very basic idea on observable. I recommend to download the source file and
see for yourself how it actually works.
Reference
http://knockoutjs.com/