The ever increasing sophistication of web applications has
increased the need for coming up with strategies to make JavaScript code clean
and maintainable. JavaScript has become
more of a first class citizen over recent years and requires the same sort of
patterns and organization that so many server side technologies have adopted
such as C# to provide elegant code. One
such pattern that emerged in recent days is the Model View View-Model (MVVM)
pattern, which provides a separation of concerns between client side model and
view data. This pattern allows the view
to bind to data in a declarative fashion, rather than using a direct
programmatic approach.
Programmatic vs. Declarative
The Programmatic Approach with jQuery
Consider the following basic example using jQuery to
programmatically set some values in html.
The script is manually setting each value of the contact object
properties (name, address, phoneNumber) to the html elements designated by
their id.
1. <script type="text/javascript" src="//code.jquery.com/jquery-2.1.0.min.js"></script>
2. <script type="text/javascript">
3. $(document).ready(function(){
4. var contact = {
5. name: "John Doe",
6. address: "1 Pebble Drive, Horsham PA 19044",
7. phoneNumber: "(215) 555-1212"
8. };
9.
10. $('#name').text(contact.name);
11. $('#address').text(contact.address);
12. $('#phoneNumber').text(contact.phoneNumber);
13. });
1. <!DOCTYPE html>
2. <html>
3. <head lang="en">
4. <meta charset="UTF-8">
5. <title>Exploring KnockoutJS</title>
6. </head>
7. <body>
8. <h1>Exploring KnockoutJS</h1>
9.
10. <div id="name"></div>
11. <div id="address"></div>
12. <div id="phoneNumber"></div>
13. </body>
14. </html>
This approach is
ok, but requires code changes when the data contract changes. It also tightly couples the code to the view
because the code is directly references elements of the user interface by their
IDs.
The Declarative Approach with KockoutJS
(Note: the KnockoutJS library requires jQuery)
In the declarative
approach, rather than manually binding data values using code, we’ll create a
ViewModel and bind it to the view using KnockoutJS. The data bindings will be specified on the
HTML elements using the data-bind attribute, indicating to knockout what model
property should be used for display.
1.
<script type="text/javascript" src="//code.jquery.com/jquery-2.1.0.min.js"></script>
2.
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
3.
4.
<script type="text/javascript">
5.
6.
function ContactViewModel() {
7.
this.name = "John Doe";
8.
this.address = "1 Pebble Drive, Horsham PA 19044";
9.
this.phoneNumber = "(215) 555-1212";
10.
}
11.
12.
ko.applyBindings(new ContactViewModel())
13.
14.
</script>
1. <!DOCTYPE html>
2. <html>
3. <head lang="en">
4. <meta charset="UTF-8">
5. <title>Exploring KnockoutJS</title>
6. <link rel="stylesheet" text="text/css" href="ExploringKnockout.css">
7. </head>
8. <body>
9. <h1>Exploring KnockoutJS</h1>
10.
11. <div id="name" data-bind="text: name"></div>
12. <div id="address" data-bind="text: address"></div>
13. <div id="phoneNumber" data-bind="text: phoneNumber"></div>
14. </body>
This approach
allows us to remove the code that is setting the value to a particular display
element and rather just specify the binding tag in the view instead. It seems fairly simple, but this is just the
tip of iceberg as to what utilizing this framework can do. We can also setup our code and bindings to
notify us when properties change in editable user interface components so that
we can take action, create programmatic control flow (if/else, loops, etc.),
create reusable user interface templates and more.
Observables
One of the key
elements of the KnockoutJS framework is an object type called an
Observable. Using an Observable object
allows Knockout provide notifications when the value of an element changes so
that action may be taken upon it. In
this example we’ll setup the interface to take the value of a text box and
update a div element.
Simple Binding Example
So let’s setup a
simple example using an observable property in a ViewModel. It’ll have a single property called “name”
that is bound to a div element which will display the text of an input box.
1. <script type="text/javascript">
2.
3. function ContactViewModel() {
4. this.name = ko.observable("Jane Doe");
5. }
6.
7. ko.applyBindings(new ContactViewModel())
8.
9. </script>
Note that the
data-bind value is setup to bind the name property of our ContactViewModel and
the div element displaying it is binding the text. As well, the initial value is set to “Jane
Doe” so that we’ll see data as soon as the page loads.
1. <!DOCTYPE html>
2. <html>
3. <head lang="en">
4. <meta charset="UTF-8">
5. <title>Exploring KnockoutJS</title>
6. <link rel="stylesheet" text="text/css" href="ExploringKnockout.css">
7. </head>
8. <body>
9. <h1>Exploring KnockoutJS</h1>
10.
11. <p>name: <input data-bind="value: name" /></p>
12.
13. <div data-bind="text: name" ></div>
14.
15. </body>
16. </html>
17. <script type="text/javascript" src="//code.jquery.com/jquery-2.1.0.min.js"></script>
18. <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
Resulting Output
Though this is a
really simplistic example it starts to paint the picture of what KnockoutJS can
provide. It removed the need to manually
retrieve the value of the text box once its content is changed and then manually
set the value into element displaying it below.
Computed Observable Binding
Let’s take it one
step further and now act on the bindings by computing a value from other values
in the model. In this example we’ll just
expand the example above a bit by adding a couple more fields and then
displaying the data from them on a single line with a computed observable value
binding.
1. <script type="text/javascript" src="//code.jquery.com/jquery-2.1.0.min.js"></script>
2. <script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js"></script>
3.
4. <script type="text/javascript">
5.
6. function ContactViewModel() {
7. this.name = ko.observable("");
8. this.address = ko.observable("");
9. this.phone = ko.observable("");
10.
11. this.contactSummary = ko.computed(function() {
12. return this.name() + ", " + this.address() + ", " + this.phone()
13. }, this);
14. }
15.
16. ko.applyBindings(new ContactViewModel())
17.
18. </script>
In the snippet of
script above note the use of the computed property of ContactViewModel called
“contactSummary.” It’s a function of the
model that utilizes the other property values and returns the output of the
function in the resulting data binding.
1. <!DOCTYPE html>
2. <html>
3. <head lang="en">
4. <meta charset="UTF-8">
5. <title>Exploring KnockoutJS</title>
6. <link rel="stylesheet" text="text/css" href="ExploringKnockout.css">
7. </head>
8. <body>
9. <h1>Exploring KnockoutJS</h1>
10.
11. <p>name: <input data-bind="value: name" /></p>
12. <p>address: <input data-bind="value: address" /></p>
13. <p>phone: <input data-bind="value: phone" /></p>
14.
15. <div data-bind="text: contactSummary" ></div>
16.
17. </body>
18. </html>
The html is
essentially the same, there’s no difference in binding to an observable whether
it’s computer or not.
Resulting Output
The output shows
how the computed observable took each of the other bound values and
concatenated them separated by commas.
Again, this is a pretty basic example of what you can do but it
illustrates the concept that you can provide functionality to manipulate an
observed property which is a power feature of the framework.
More to come…
I explored some of
the basic concepts of KnockoutJS, though there are quite a few more features
available including the ability to define element templates to create reusable
chunks of UI elements that include programmatic control flow. In the next entry I’ll explore a bit deeper
and check out some of the more powerful functionality of the framework.
Labels: JavaScript, jQuery, KnockoutJS, MVVM