Knockout.js pro tips - working with observable arrays - StrathWeb

Strath

July 29th, 2012

Knockout.js pro tips – working with observable arrays

Because array manipulation doesn't have to be a bottleneck

Recently, I have been blogging mainly about Web API, but many people have been asking me about Knockout.js. And rightfully so, because it is one of the most robust Javascript client side technology right now.

I blogged about KO a couple of times here before, but I thought it might be a nice change to do it in a new format. Instead of a one big, end-to-end solution, let’s do a set of “pro tips”, small pieces of real-life advices for your Knockout solutions.

This article assumes a working knowledge of Knockout.

Tip #1. Performance improvement for ObservableArrays – work with underlying arrays

It is easy to suffer from performance problems when manipulating large or rich (containing complex objects) observable arrays. Any time you perform any operation on such array, all the subscribers get notified and very often an avalanche of computations and UI updates is the consequence. In the development process, such a chain of events is normally not a visible bottleneck, until you get to a a rich, real-life user interface, where suddenly lags are causing problems in your application.

Imagine you are inserting 100 items into an observable array. More often than not, you don’t need each subscriber to recalculate it’s dependencies 100 items, and UI to be reacting 100 times. Instead, once should just fine.

To do this, you can always modify the underlying array instead of the observableArray directly, since observableArray concept is just a function wrapper around the traditional JS array. After you are done with the array manipulation, you can then notify all the subscribers that the array has changed its state. See the simple example:

In this example. we are using KO extensions to push all values received via XHR call into the underlying array, and then notifying the subscribers using valueHasMutated().

This is an example copied from an actuall application. The JSON here contains all NCAA Divison 1 basketball teams (346 of them), we don’t want to burden our UI and the browser to recalculate everything 346 times, which would be the case if we just looped through the results and added each item one by one.

Tip #2. If possible, throttle your computed observables

This is not really speicifc for observableArrays, but all observables, but it is usually most evident with computed observables which depend on observableArrays and return a different array. Again, consider the following example, which allows users to filter the teams based on the text entered into a textbox (quite a common scenario):

We have a filteredTeams computed observable, which is dependant on the main teams observableArray and on the filterText observable.

Any user change to the filter text in the textbox will trigger a considerable amount of calculation to be happening (remember from previous example, our teams observable array has 346 items). As the user types new characters, the whole process get repeated. Note that you might have additional subscribers, so the amount of calculations could be even greater.

In such cases, especially if you are willing to support less computation efficient browsers such as older versions of IE or mobile browsers (they will not have as much CPU at their disposal as computer browser) you ushould really consider using Knockout’s throttle extender. Applied to a computed observable, it defers the execution of the dependencies by n milliseconds.

So in our example if the user types in a team name, he won’t really get any filtering until he stops writing for the given amount of milliseconds – in this case below, 750. This way we could save redundant CPU cycles to i.e. filter the array by starting letter “A” and so on.

Tip #3. Array manipulations made easy

There are a lot of out-of-the-box functions that make working with observable arrays (and arrays in general) very easy. Those are located in the ko.utils namespace, so a lot of people are unaware of them, as they won’t come up with intellisense after adding a “dot” after your observableArray.

The functions in the ko.utils namespace include, among many:
– looping helpers
– filtering helpers
– searching helpers – possibility to match first result or get unique values
– array comparison utilities

You may have noticed that in the previous two examples I actually used ko.utils.arrayPushAll, ko.utils.arrayFilter and ko.utils.stringStartsWith. Another very useful one is ko.utils.arrayMap as it allows you to project your observableArray (or, for that matter, regular array as well) onto some other array and use that. This is especially helpful for computed observables.

I will not go into deep details about ko.util as there are already great resources about that. RP Niemeyer, the Knockout guru, has a terric article about that so I really recommend you check it out.

Tip #4. ObservableArrays are extensible

You are not confined to using only the array manipulation functions that knockout provides out of the box, you can aeasily add your own.

Let’s say you want to add custom sorting to your observableArray. You are holding custom objects in that array and would like to sort them in the array based on one of their properties.

In our example the objects to be sorted look like this:

Let’s extend observableArrays to provide this custom sorting:

Now you can invoke the sorting by simply calling (where vm.teams is your observable array):

Summary

Working with arrays in knockout is a very broad subject, however I hope these few tips could help you get on the right track. There are always many ways to achieve things in javascript, but choosing the right tools in knockout.js can make your life much easier.

Additionally, when working with large data sets, complex collections of objects and rich user intefaces it is very easy to run into performance problems with knockout.js. Thankfully there are ways to avoid these, and smart array manipulation, rather than brute force can be one of the best performance boosts for your application.

Be Sociable, Share!

  • http://greatrexpectations.com Steve Greatrex

    Do you find that the use of the throttle extension makes unit testing more complicated? How do you work around that?

    • Filip W

      Yeah, that’s a good point. The way I normally work around that is I use a setTimeout in the the tests to custom delay its execution and thus mock different behavior types that throttle is expecting.
      In principle, that’s exactly what throttle does – throttled updates are scheduled individually with separate setTimeout calls.

      So while normal tests would run instantaneously, I would have another test which would pause for let’s say 500ms and perhaps another one which pauses for 1000ms, all depending on the throttle settings.

      • mozily

        I think we could modify the ‘extend’ method and remove the trottle in the tests

  • Pingback: Dew Drop – July 30, 2012 (#1,374) | Alvin Ashcraft's Morning Dew

  • http://kamranicus.com Kamran Ayub

    For working with the underlying array, check out my Underscore.KO Nuget package and library… it basically adds Underscore.js support to KO arrays and it does it properly, modifying the underlying array and not the observable itself.

    https://github.com/kamranayub/UnderscoreKO

  • Pingback: Friday Links #214 | Blue Onion Software *

  • Pingback: Knockout webosphere « TechnoBuzz

  • Bebekı

    Excellent post, very informative.

  • Pingback: Closing out Knockoutjs Adventure « TechnoBuzz

  • http://nonstopwords.com wordsearch puzzle

    I have a problem with KO,so I want my html section to be hidden at the start.

    when using visible propery binded to a computed value, it displays the html section for a fraction of the second and only after that is hides it. even though it is set to visible:FALSE at the page load event…

    • Filip W

      Well, set it to be hidden with CSS by default, and then bind to knockout.

  • ypradeep23

    I am looking for some ways to implement sort on multiple properties. Such as vm.teams.SortBy(“name”).thenSortBy(“location”).thenSortDescBy(“id”).

    can anyone guide on this implementation?

  • Brian

    Thank you!

  • Vidmantas Drasutis

    Hi, nice article. Can you print how your data looks like. And if my data is like:

    var persons = [ {"ID": 1, "FirstName": "Jonas", "Interests": [{"Title": "Sport", "Description": "Play basketball"}, {"Title": "IT", "Description": "I am intrested in new technologies"}] }, {“ID”: 2, “FirstName”: “Tomas”, “Interests”: [{"Title": "Cars", "Description": "driving"}, {"Title": "IT", "Description": "something in details"}] } ];

    What I have to write in line 8 in my case “ko.utils.arrayPushAll(array, response.sports[0].leagues[0].teams);” ?

  • Pingback: Observable sorting | Infinityservic

  • Hnin Le Kyaw

    Thank you very much! Your simple example could solved my problem in getting values from web API into the observable array.

  • Pingback: Knockout.js ObservableArray Performance « .Net'ers