Announcement

Monday, January 23, 2012

Emberjs vs Knockoutjs

Originally Published on BootstrapToday Blog

BootStrapToday is around for about 2 years now. Recently we have been working on making it faster. We have done many small changes and will continue to make improvements. We are also working on redesigning the user interface to make it simpler for the user and more maintainable for us.

Some background:

BootStrapToday uses django and postgresql on the backend. On the browser side, we have used jquery and a lot of custom javascript and ajax calls. All this custom javascript is slowly becoming difficult to maintain. Also making sure everything works on all major browsers is difficult. For example, today when a user uses ‘quick edit’ to edit a ticket and submits it, updating the status of tickets in the ticket list requires an ajax call to server to get updated list. This  increases load on server. Later this year we are also planning on creating a mobile app.

Hence we are  redesigning the UI and have some idea of how things will look like. We are also  looking around for a javascript framework that can help in making these ideas into reality. I started by looking around and got a useful comparison of various javascript MVC frameworks.  After studying for some more time, I decided to do a quick prototype of ticket list and ticket details+edit display using the Emberjs(SproutCore 2.0) and Knockoutjs frameworks. I am not an expert in Javascript. Hence it took me some time to create two prototypes. The code is probably not the ‘best examples’ of using Emberjs or Knockoutjs but it gave us an insight into what will work for us.

Specifically we were looking for following features.

  • UI Bindings: Client side Template, declarative bindings to models and auto updating views.

  • Works well with Django.

  • Works with jquery and other existing components.

  • Ajax.

EmberJs (SproutCore 2.0):

Ember is javascript MVC framework. As the Ember introduction says
Ember makes it easy to divide your application into models, views, and controllers, which improves testability, makes code more modular, and helps new developers on the project quickly understand how everything fits together. The days of callback spaghetti are over.

Ember also supplies built-in support for state management, so you'll have a way to describe how your application moves through various nested states (like signed-out, signed-in, viewing-post, and viewing-comment) out of the box.

First problem I faced with Ember is the ‘template syntax’. Ember uses Handlebars as template engine. At places Handlebar template syntax is similar to Django template syntax. So Django tries to parse this template and interpret it.  So the possible solutions were:

  • Use a different template system than Dajngo’s default template system. This was not a viable option since it meant that we  throw away all existing templates and also  educate the team on different template syntax.

  • Needed a way to tell Django ‘don’t interpret’ this part of the template. Unfortunately there is no good standard way to do this in Django. I ended up in using the ‘raw’ template tag described in ‘http://www.holovaty.com/writing/django-two-phased-rendering/’. However, this code is GPL licensed. We can use it for prototyping but we cannot include it in our product.

BootStrapToday uses slightly older version of jquery. It seems Ember requires jquery
1.71.6+  (Edit : Peter Wagenet has clarified that Emberjs works with jquery 1.6+ ).

Ember properties and computed properties worked well. Also, I implemented a computed property which when called, queried the necessary data from server using an ajax call. However, it means every time when a property value is queried, it will result in an ajax call. Obviously that’s not a very good idea. However, Ember has a nice touch that you can make a property ‘cacheable’. Once you marked a property as cacheable(), it stores the last returned value and reuses that instead of computing it again. That worked well for reducing the number of ajax calls.

Then I tried to implement Ticket edit functionality using the existing Django form. Currently Ember documentation is very limited on how to use various HTML input fields, and there is no information on using existing html forms with Ember properties. I could not figure out how to reuse the existing  Django forms. It means I had to re-implement the forms as Handlebars templates and input field ids etc. have to match with ids that Django expects. Other option was to write custom form fields which will generate the Handlebars template code rather than HTML generated by default Django fields.  This means a lot of work and not much benefit. This is one area where Knockoutjs declarative data-binding shines.

KnockoutJS

Knockout (KO)  is javascript MVVM framework.  KO Observables page has a small introduction to MVVM pattern. Key features of KO as described on the KO Introduction page are:


  • Elegant dependency tracking - automatically updates the right parts of your UI whenever your data model changes.

  • Declarative bindings - a simple and obvious way to connect parts of your UI to your data model. You can construct  complex dynamic UIs easily using arbitrarily nested binding contexts

  • Trivially extensible - implement custom behaviours as new declarative bindings for easy reuse in just a few lines of code.


The big advantage that KO has over Ember is the documentation. The documentation and Live Examples are a GREAT way to learn and experiment with KO.

KO template syntax is different than Django templates. So using KO with Django templates is simple.

KO Observables is somewhat equivalent to Ember properties. However there are few key differences. In Ember, the dependencies of properties are defined by the developer. In KO Observables, dependencies among observables are automatically determined.  Now this is good and bad. The good part is that as a developer you don’t have to worry about explicitly determining and defining dependencies.

KO Computed Observables documentation says following about the dependency tracking.

It’s actually very simple and rather lovely. The tracking algorithm goes like this:

  1. Whenever you declare a computed observable, KO immediately invokes its evaluator function to get its initial value.

  2. While your evaluator function is running, KO keeps a log of any observables (or computed observables) that your evaluator reads the value of.

  3. When your evaluator is finished, KO sets up subscriptions to each of the observables (or computed observables) that you’ve touched. The subscription callback is set to cause your evaluator to run again, looping the whole process back to step 1 (disposing of any old subscriptions that no longer apply).

  4. KO notifies any subscribers about the new value of your computed observable.

The line in bold has a side effect.  Let us suppose that I want to load some property from server (like description of a ticket) when it is accessed first time. Now since KO invokes the evaluator function immediately, it means it will trigger an ajax query to the server even though user may not need the property. Since KO invokes evaluator function, to get the dependencies of Computed Observable, if there is some way to manually define the dependencies then this evaluator call is not required. I could not find a way to do this. Hence I have to do some tricks to load make necessary data from server when user needs it. Basically I load the data in some click handling functions.

Also I could not find anything equivalent to ‘cacheable’ in Ember.

At this point loading a ticket list from server and displaying the details when a ticket is selected from list was working using KO and Ember.

Then I started implementing ticket edit functionality and here KO has significant advantage over Ember for our needs.  Because KO uses declarative data binding, attaching KO observables to an existing Django form was trivial and I could reuse existing Ticket edit form.  For testing I used a simple function like the one given below to add ‘data-bind’ attribute to various form fields. Then I called the ‘form.as_table’ in template and the bindings worked like charm.
def add_databinding(form, fld_observable_map):
'''
add data binding attribute to all form fields.
'''
    for name, field in form.fields.items():
        if name in fld_observable_map:
            field.widget.attrs['data-bind'] = "value:%s" % fld_observable_map[name]

Both KO and Ember are useful and interesting frameworks (something you should keep an eye on). However, today KO satisfies our needs better than Ember.

Now we are working on a django app to simplify using KO with Django. I will keep you updated on any interesting things that happen along the way.
Updates: BootStrapToday new design is based on Knockoutjs. You can watch the video below to see how new design based on KO looks like.




You can also signup for Free to test drive new design by clicking below

Signup for Free

19 comments:

Peter Wagenet said...

I'm on the Core Team for Ember and I wanted to apologize for our lack of proper documentation. It's something we're aware of as a problem and are working on it. When working on a new project it's hard to both write code and docs!

You should actually be able to get Ember to play pretty nicely with existing HTML, how much you have to convert to Ember Views is largely up to. However, it looks like in the case of Django (which I'm not very familiar with), you're declaring you're already using some sort of templating language instead of raw HTML. This would definitely make integration more difficult.

You also mentioned that Ember is limited to jQuery 1.7, it actually works with jQuery 1.6+.

nitinbhide said...

Peter,
I completely agree that 'for a new project it is hard to do both write code and docs'. Emberjs code is very readable. So for early experimenters a readable code is a good enough starting point. Emberjs team is doing a great job. My main trouble was integrating with Django templates and integrating with HTML generated by django forms.
Thanks for the correction about jquery version. I have updated the article.

Graeme Foster (@GraemeF) said...

This really nice onDemandObservable solves your problem with deferred loading in KO. Works very well, and it gives you another observable to watch to indicate that it's loading!

http://jsfiddle.net/rniemeyer/BDrvT/

Mohammed Khan (@khan_io) said...

If I followed correctly, the problem of getting client side templating to work parallely with Django gets messy due to conflicting template tags, but if you can get custom template tag descriptor like @ericflo 's Verbatim https://gist.github.com/629508 which is for jQuery Template, I am sure the same can be done for any javascript templating library.

nitinbhide said...

For Emberjs prototype I solved it using the 'raw' tag described in ‘http://www.holovaty.com/writing/django-two-phased-rendering/’. I think this is similar to 'verbatim' tag. However, I could not bind the Emberjs views to Django forms. The options I considered were (a) rewrite all forms using the Handlebars template or (b) to write a Django custom fields to generate the Emberjs views code rather than standard HTML. I think both options are time consuming especially compared to declarative data-bind of Knockoutjs. So far my particular problem Knockoutjs is better suited than Emberjs.

Idei de afaceri said...

This should be nice if you put it on video with more explanations

Ryan Niemeyer said...

Just wanted to share a couple of KnockoutJS tips:
-you can define a computed observable in a way that it does not evaluate immediately either by doing:

var myComputed = ko.computed(myFunction, myTarget, { deferEvaluation: true });

or

var myComputed = ko.computed({
read: myFunction,
owner: myTarget,
deferEvaluation: true
});

-as far at the cacheable part: computed observables are always cached in Knockout. The initial evaluation does the first cache of the value and the actual logic will not run again until it is notified that one of its dependencies has changed.

Nice article!

nitinbhide said...

Hi Ryan,
Thanks. I did not know about the 'deferEvaluation'. Will try it out.

nitinbhide said...

Graeme,
For the time being my problem was solved with 'deferEvaluation' flag mentioned in Ryan's reply.

Still onDemandObservable looks great. For my experiment, I did something similar, but it was not as reusable as 'onDemandObservable'. I think I am going to need this also.

THANKS.

nitinbhide said...

somehow never thought of making a video. I will give it a try. Thanks for the suggestion.

Alex said...

I have jumped into using Emberjs over the last few evenings, and while the documentation is extremely lacking (and would speed up the learning curve incredibly) The readability, verbosity, and organization of the code, has helped me pick it up really pretty quick. Also, the partial, but most extensive docs i found at http://ember-docs.herokuapp.com/ have been a decent help - (Although its like the android docs which is not intially intuitive in presentation)

steve s said...

I went through a similar scenario 6 months ago where i came up with a choice of 3 frameworks - knockout, ember and backbone. In the end i struggled to find which would be best for a 'long term project' scenario. I was leaning towards ember however it was another developer who suggested backbone which i previously dismissed as it seemed too light. I was wrong and infact it has great documentation and fits really well with common js standards. Have look into backbone with require.js.

george1 said...

Why knockout wasn't a valid candidate?

nitinbhide said...

Knockout WAS a valid candidate. In fact, the new UI of BootStrapToday is built with KnockoutJS. Check out the new look at http://blog.bootstraptoday.com/2012/04/22/bootstraptoday-v3-0-alpha-released/

Cheatsheet: 2012 05.16 ~ 05.31 - gOODiDEA.NET said...

[...] Emberjs vs Knockoutjs [...]

daslicht said...

How about dealing with SEO ?
Is there a smart way to present indexable content without having to create anythi9ng twice?

nitinbhide said...

I evaluated Emberjs/Knockoutjs for using in the BooStrapToday rewrite. Since content of BootStrapToday accounts is not indexable, we did not consider this factor at all.

For now, Google's ajax crawling specification seems to be the best option available for making Ajax content indexable.

https://developers.google.com/webmasters/ajax-crawling/docs/specification

Alinik said...

It seems Django 1.5(alpha) adds standard tag for compatibility with JS frameworks. this may solve that issue too.

Jax said...

If we're voting then I personally dislike videos, LOVE the text. Thanks for the article! :)