Angular 1.x Performance, ng-repeat Performance and Optional one time binding

angular

Dear Angular users or those who are wondering if they should use Angular.

Well, for those who are hesitating, there’s no way to hesitate anymore. If you follow the rules below, you’ll have no issues related to AngularJS performance.

In addition to this, compared to other alternatives, AngularJS has a huge ecosystem, you’ll find lots of modules that will suite your needs (analytics, design, internationalization, REST API, etc…). That’s something we consider a lot at Wishtack. It could be fun to develop Wishtack in “Go” language but there’s almost no ecosystem and that’s why we chose Python and Django (we use tastypie to handle the API, mongoengine to handle our mongodb using objects, gevent instead of multi-threading or callback waterfalls we would have had with nodejs etc…)

And of course, AngularJS will help you separate concerns, display is handled in html template files, functional stuff is handled by javascript controllers and the server through a well crafted API (we’ll talk about this in a future post).

Let’s go!

When you bind a value from your current scope to the template, that’s when the trouble starts concerning AngularJS performance.

For example, when you do this:

<div>{{ myValue }}</div>

Every time, a digest is triggered (this happens when anything changes in your view), AngularJS will try to see if “myValue”‘s value has changed and it can be very expensive if you use some function that does something funky like calling the API (yes, why not!?).

<div>{{ user.getFirstNameFromServerOnMars() }}</div>

Imagine how expensive it can become if you are using an “ng-repeat” generated list using:

<ul ng-repeat="user in myReallyLongUserList">
	<li>{{ user.getFirstNameFromServerOnMars() }}</li>
</ul>

That’s where the “relatively new” one-time-binding feature introduced by AngularJS in the 1.3 version, will help us.

Using the special one-time-binding syntax (“::” prefix), our template becomes:

<ul ng-repeat="user in myReallyLongUserList">
	<li>{{ ::user.getFirstNameFromServerOnMars() }}</li>
</ul>

Now, with one-time-binding, once the interpolated value of “user.getFirstNameFromServerOnMars()” is different to “undefined” (note that “undefined” is strictly different to “null), this will tell AngularJS that the value will not change anymore thus even when performing a digest, AngularJS will not try to update that value anymore.

You can check the number of watchers (number of expressions that are watched by AngularJS) using ng-stats. By the way, be careful to disable Batarang (AngularJS debugger) as it disables one-time-binding and will not give you the real number of watchers when using one-time-binding.

You can also apply one-time-binding to the list you are using in “ng-repeat” with the same syntax if the list never changes:

<ul ng-repeat="user in ::myReallyLongUserList">
	<li>{{ ::user.getFirstNameFromServerOnMars() }}</li>
</ul>

But if the list can change, then you should use AngularJS’s “track by” in order to tell AngularJS not to update the whole list but only the added or removed item.

<ul ng-repeat="user in ::myReallyLongUserList track by user.id">
	<li>{{ ::user.getFirstNameFromServerOnMars() }}</li>
</ul>

Now that your AngularJS performance troubles are over, let’s talk about optional one-time-binding.

On Wishtack’s friends wishlist we use a directive that shows information that tells if the wish has been promised etc… on that list, for AngularJS performance’s sake, we use one-time-binding as data will not change in that view.

The extra information is displayed using a component like this:

<wt-wish-added-promised-info one-time-binding="true" ...=""></wt-wish-added-promised-info>

But we use the same directive in the wish detail view where the data might change if the user clicks on “promise” button and it looks like this.

And here we are using:

<wt-wish-added-promised-info one-time-binding="false" ...=""></wt-wish-added-promised-info>

Note that “one-time-binding” attribute can be set dynamically.

What’s the magic behind this? Well it’s simple. In the component’s template, we simply do this:

<div ng-bind="{{ ::oneTimeBindingPrefix }}{{ ::'myValue' }}"></div>

And tada! It works!

Well, we thought about open-sourcing a directive for this but sometimes you might use this in “ng-bind”, sometimes in “ng-show” and sometimes in a directive we don’t even know about so we thought that sharing the trick would be enough.

Watch it running live on Wishtack!

Does this help you? Let us know!

See you soon!

Advertisements

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