Knockout.js – A Year Later

It was about a year ago I started to learn Knockout.js.  I primarily develop with an ASP.net web stack of technologies.   Moving into MVC 4 I noticed Microsoft had started to ship Knockout.js with the MVC 4 template.  Once I read and understood what Knockout.js was and how it could help me things really started to click.  I’ve implemented Knockout in a few complex javascript applications I maintain over the past year.  I’m going to share my experience with moving from solid jQuery to Knockout.

The old way of doing things

You see I fell into the same hole that many developers found themselves using jQuery.  When you start to develop a complex javascript application jQuery becomes spaghetti code very quickly.   Manually mapping variables to reflect state,  referencing DOM elements directly, manually checking boxes or setting input values with a complex UI is incredibly difficult to maintain.   I know this because unfortunately I have a lot of code in the wild that is written like this.   It isn’t broke, it works well actually, but it took a long time to write and it isn’t fun to come back to and modify.

The new way is better – Using Knockout

If you don’t know what Knockout is then I’d recommend you just visit their site and read about it.  In short I feel like the cornerstone of what Knockout does is the observable pattern and dependency tracking.  This allows you to create a view model, bind the view model to the DOM, then freely use knockout’s data-bind syntax declaratively directly in the DOM.   The real magic happens though when you check a checkbox or change a drop down.  The new values are bound to these elements and values are updated automatically for you.  Your UI can react to these changes.

If you were like me moving from the mentality of micro managing your DOM and UI state it is very hard to let go and trust knockout to do the work for you.  In fact it took me awhile to even wrap my head around what is going on in the background.

Knockout saved me money

I typically don’t share actual work I’ve done, but this is a business I co founded a few years ago.  I no longer have a stake in it, but I’m still doing all the web development.  I recently revamped the entire site – http://bandegraphix.com.  More importantly though is the Knockout based lettering tool I wrote which is a fork of a tool I have in production on several other sites also.  On the surface it is deceptively simple, however, a tool like this takes a lot of boilerplate jQuery and javascript to hook everything together.  The UI must fetch an image and get a price anytime something changes.  When the design is saved the entire design has to be sent back to the server and saved.  If the design needs to be edited the design must be loaded from the DB and the UI state brought back to exactly how it was left.

bandegraphix

 

I’ve written tools similar to this one for other customers in pure jQuery and I can tell you it took me about one third the time to write this tool that has more bells and whistles than most any other I’ve made.    Don’t get me wrong there are some serious gotchas with knockout that I will cover in a second though, but overall I was able to write this tool and be really confident with how it worked.  Even better was the huge js file to keep everything wired together simply disappeared, Knockout does it all for you.

When I previously had to write a tool like this I had to load the design with all of the selected options from the database then manually map these values into javascript, then call a method to properly change the state of the checkboxes and inputs.  With Knockout it just worked.  Yep, it literally just works. I load a view model from the database via an AJAX method and the entire UI returns to the same state it was when I left it.

Less Code & Less Duplicate Code

Because of Knockout’s declarative syntax and observable bindings you simply write less code.  To update a checkbox or input requires a simple statement rather than a brittle by id jQuery DOM selector.   Knockout’s powerful template binding allows you to reuse pieces of html efficiently.  In my case of the vinyl tool above I load all of the UI elements in what is essentially a toolbox of components then use knockouts template binding.  This allows me to efficiently swap out and reuse components in other tabs or create entirely new tools with very little repetitive code.

Random Thoughts and Gotchas

Debugging Knockout is Hard (my biggest gripe)

Knockout essentially runs in its own binding context during run time.  The effect of this is clear if you try to utilize firebug or chrome developer tools to debug your application.  $root, $parent, etc have no meaning because you aren’t operating in the context of the binding from the console.  For this reason it is difficult to use the traditional methods of debugging javascript.  Breakpoints and exceptions don’t occur in any meaningful way from the console window.  Knockout will throw a semi useful binding error, but it can sometimes be difficult to find the cause directly.  Often I will place console.log() calls wherever I can to isolate the error.

Variable or Variable()

Personally my biggest source of Knockout issues is using variable or variable().  In Knockout you should use () when reading the value of an observable in code, however when using the declarative syntax in the DOM you don’t have to use the ().  Since the observable function is essentially wrapped over the variable to give it the observable behavior this is required.

Computed Variables and Throttle

Computed variables in my opinion are the biggest “magical” part of Knockout.  Unfortunately it is pretty easy to get bit by a computed variable.  For example perhaps your computed variable makes an ajax call per UI update.  Again, refer to the site example I posted above the text box where you type your text updates per key press.  Initially when I wrote this and typed a sentence my server got hammered every key press!  It is a great idea to use the throttle feature to solve this issue. Throttle will delay an update until the last keypress for a period specified.

Ko.Mapping and dynamic view models

Perhaps my biggest regret in terms of added complexity is utilizing a dynamic view model.  Most examples of Knockout you’ll see simply utilize a hard coded view model.  With the site I showed off above I took a different approach.  I didn’t want to have to maintain a server side view model and a client side view model.  My solution was to take a master view model to coordinate the view, handle navigate, saving, loading, etc.  I then created a single observable to hold my child view model that is retrieved from the database.  The definition of this model remains server side and is only defined in one place.  In theory this sounds good but I question if it was a good idea, mainly because of the way Knockout maps complex objects.

In order to make this work you must use the ko.mapping plugin.  When you retrieve the model from the database it must be mapped into observables. For the most part this is handled for you, however I found that arrays of complex objects did not map to observable variables as expected.  My advice, make everything observable from the start and you’ll have less problems.  I had to solve this by creating a pretty large piece of code to handle the mapping of the object.  Every time ko.mapping is used it refers to the map as to how the object is constructed.  In the end it might have been easier to simply write out the child view model by hand.

Use BindingHandlers instead of accessing by Id

When starting out with Knockout it can be very tempting to mix classic jQuery $(‘#someid’) calls.  You don’t need to do it and the proper way is to write a Binding Handler to do the job.  You’ll still be able to accomplish whatever you are wanting to do and in the end you’ll have a reusable piece of code.  If you properly implement the binding handler then when your UI changes you won’t have to worry about things loading correctly.

In the case above I utilized binding handlers to do several things.  I use several jQuery plugins, but never reference the DOM element directly.  For example I use the jQuery tabs feature and created this simple handler to handle them.


ko.bindingHandlers.tabBinding = {
init: function(element, valueAccessor, allBindingsAccessor, viewModel, bindingContext) {
var options = valueAccessor() || {};
$(element).tabs(options);
}
};

Moving forward

A year later a lot of other frameworks have cropped up and cemented their place.  Angular is the first one that comes to mind.  I’ll be honest I haven’t used Angular and from what I’ve read I’m not sure it would be the best fit for what I did on this project.   Mainly because Angular isn’t the same thing at all, it is a client side MVC framework, not an MVVM.  My interest was reducing boilerplate code and writing more efficiently, I believe knockout allowed me to do just that.  My app wasn’t necessarily changing views, rather it consisted of one complex view.

If you are striving for more of a single page app however, then I do believe there are better options out there for that.   If you like Knockout then the logical choice is moving to Durandal.  Durandal takes several popular libraries (knockout, sammy, and require) and hooks them all together to make a nice MVC framework.  I personally used sammy and knockout in the project above, but I did feel like what I was doing lacked a lot of structure.  Durandal will bring in a nicely designed MVC system but keep the familiar knockout style bindings.

 

Summary

In summary a year later with Knockout I’m really glad I invested the time to learn it and use it properly.  It has truly saved me time and money by producing less code with fewer bugs.  If you or your team is still writing spaghetti jQuery then you need to take a look at this!

 

 

Be Sociable, Share!
Tagged: , , ,

Discussion

No comments yet, be the first.

Add a Comment

*