KnockoutJS: making JavaScript a bit less crappy

If you are JavaScript fan, let me explain myself before you tie me to the pole in the middle of stack and start the fire. I just rather don’t feel this language. Perhaps if I had to write more projects in it and familiarize more with various libraries, my opinion would be more neutral but well… I’m just too tainted by Erlang. Imagine my frustration when I was looking for some bug in the JS code, which was caused by lack of “return” before expression at the end of function definition. I just got too much used to thinking that whatever I write as last expression of the function, it will be returned without some “return” keyword.

Those who know me, already guessed I’m writing about my experiences with implementing Erlang Central Cafe. First version was an effect of cooperation with Stefan Strigler. Do I have to mention that after about two-year break in JavaScript programming and zero knowledge of Google Closure library I virtually crippled Stefan’s code. πŸ™‚ It worked but I was bit ashamed of how it looked internally. πŸ™‚ Finally I made a decision: full rewrite using KnockoutJS!

With somewhat different set of requirements than at the beginning of the project, I could create quite different class hierarchy with clear Model-View-Controller separation. Since we are going to open the source code sooner or later, I tried to make it as generic as possible – so other developers will be able to put their own chat solution together with connection and room controllers, models and views. When I thought of implementing models and views I instantly remembered a project where I used KnockoutJS. It was a bit less then two years ago and back then the library managed modal window display for user registration/login. Even though it was still JavaScript, it was a really cool experience.

KnockoutJS has excellent tutorials, which makes you want to try all these nice tricks in your application. For those who don’t know this library yet: It allows you to create objects in your model called “observables”. Whenever they are updated, KO will automatically modify the view. All you need is to declare bindings in your views like <span data-bind="text: firstName" />. You have a really wide range of bindings to choose from and if you need something really strange – you can write your own binding!

I want to share with you some tricks/caveats, so you will be able to save time I had to spend at some point. πŸ™‚ From now on I will assume you already have done KnockoutJS tutorials and know the basics.

Apply bindings to specific node
It is a bit hidden in documentation, because the official way of applying bindings is to invoke ko.applyBindings(myViewModel); to apply the model to whole document. If you want better control of what is applied where, use ko.applyBindingsToDescendants(model, domElement);. It will bind data only to descendants of domElement, which may be for example obtained with jQuery.

Scroll window to bottom after view update, a.k.a. call function after view update
Unfortunately for this one you have to use template functionality, because only with them you can use “afterRender” property, where you can specify callback to be executed after the update. You can also subscribe to observable change event but here is a trap! The function subscribed to the observable will be invoked before the rendering, so it is not possible to keep window scrolled to the bottom after new message arrives (you can imagine it is quite important in chat application :)). The binding is following: data-bind="template: { name: getMsgTmpl, foreach: messages, afterRender: afterRenderCb }". See the getMsgTmpl? Read below.

Dynamically choose template for binding
You don’t have to provide specific template name in the binding. It can be also a function name and this function can return template name. In Erlang Central Cafe it is used for choosing which of the following templates use: user message, room message or “GUI message” (the info about external Jabber clients is a GUI message).

Snippet for scrolling window to bottom
First of all you have to detect if the view is scrolled before the update, so we won’t annoy the user, who wanted to see earlier messages intentionally. This is where observables subscription is useful:

this.messages.subscribe(function(newVal) {
    var el = self.view.logElement();
    self.isScrolledToBottom = (el.scrollTop == el.scrollHeight - el.clientHeight);
});

And here is the scrolling function:

mongoosechat.view.ChatBox.prototype.scrollToBottom = function(checkIfBottom) {
    var box = $(this.uiChatboxLog);
    box.scrollTop(box[0].scrollHeight+100);
};

If KnockoutJS was a human being, I would kiss him/her/it. It was an amazing relief to forget about appending DOM nodes to chat log and worrying if correct values are displayed. Now it’s transparent and required me to spend only a few hours of learning the library. I remember someone saying (regarding garbage collection) that there are people who consider memory management too important to leave it to computer. But the truth is, memory management is so vital, we can’t leave it to human (right, C/C++ developers?).
I could paraphrase it today: Displaying view is so vital and prone to errors, we can’t just let human being do all the updates manually. Praise KnockoutJS for returning part of my faith in JavaScript. πŸ™‚

Advertisements
This entry was posted in Development, Webdevelopment, XMPP and tagged , , , . Bookmark the permalink.

One Response to KnockoutJS: making JavaScript a bit less crappy

  1. AR Login says:

    Hey nice post. I hope it’s alright that I shared
    this on my Facebook, if not, no issues just let me know and I’ll delete it.
    Regardless keep up the good work.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s