Performance tips for AngularDart

In most use cases, AngularDart will run fast enough for you to not even notice that there is a framework under the hood. However, when dealing with large DOM trees such as tables or long lists (let's say thousands of DOM elements), you can get hit by Angular very hard.

The basic ideas behind optimization is that we want to reduce the number of expressions that Angular watches and try to keep the scope hierarchy shallow. Internally, Angular uses the so-called digest loop, which is executed every time a value in the model changes. It has to check all watched expressions and see whether they've changed, and if they did, it has to propagate the change further until there are no pending changes.

When we talked about scopes, we mentioned that some Angular directives create new scopes and when used inappropriately, they might have significant performance impact. That's why we'll take a look at some performance optimization tips that reduce the number of watched expressions, together with a few recommendations on what you shouldn't do in order to speed up you application.

You can check out which directives create new scopes in the documentation at https://docs.angulardart.org.

Avoiding nesting ng-repeat directives

It might seem logical to display a table like this:

<table>
  <tr ng-repeat="row in rows">
    <td ng-repeat="col in columns">{{ row.columns[col.id] }}</td>
  </tr>
</table>

This is an absolute overkill for Angular. With 100 rows and five columns, this makes 500 scopes. If you added just a single ng-if directive inside <td>, it would be 1,000 scopes because each ng-if creates a new scope too.

Instead, try to preprocess your templates. Of course, you have to use ng-repeat to actually print each row, but you can modify the template to use just one ng-repeat directive:

<table>
  <tr ng-repeat="row in rows">
    <td>{{ row.columns[1] }}</td>
    <td>{{ row.columns[2] }}</td>
    <td>{{ row.columns[3] }}</td>
  </td>
</tr>

For very large lists, you can also use Polymer's <core-list> element that we saw in the previous chapter.

Using track by for ng-repeat

In the to-do list example, we used a filter to sort tasks by their time. A big disadvantage of this approach is that every time you change the sorting order or add/remove a task, Angular has to remove the entire DOM subtree and create it again.

This is why you can use a special track by keyword, which tells ng-repeat how to uniquely identify each row and therefore, when you manipulate the iterated collection, it can check whether the row was already created. So, a better and recommended way to use ng-repeat in the to-do list would be:

<li ng-repeat="t in tasks|filter:term|orderBy:p track by t.id">

Avoiding excess formatter usage

Another common performance bottleneck that isn't obvious is the excessive use of formatters, usually in loops. For example, let's say we have a formatter called niceDate that turns a timestamp into a date according to the user's locale:

<li ng-repeat="time in collection">{{ time|niceDate }}</li>

Firstly, Angular doesn't know that the formatter won't modify the original variable. This is the same problem that we already saw with NgOneWay, as described earlier. Your formatter might change an object's property and Angular has to process it in a digest cycle. This is why Angular actually runs formatters twice.

Concretely, you change an object's property in your model and Angular evaluates an expression with your formatter. But you might change an object's property again in the formatter and Angular has to be sure it caught all the changes that occurred, and therefore, it has to evaluate formatters again.

Secondly, this formatter doesn't even need to be used in the expression. You can format the date somewhere in your code and just print the formatted date.

Not everything needs to be in Angular

When using Angular, things work like magic. Although you shouldn't modify the DOM tree that is already maintained by Angular, you still can change CSS styles or classes (again, those that aren't already managed by Angular; otherwise, it might lead to unpredictable behavior) by yourself.

For example, if you wanted to display a list with 1,000 items and then filter (show/hide) them in some manner, you can use this code:

<ul>
  <li ng-repeat="item in collection track by item.id"
      ng-show="showMe(item)">{{ … }}</li>
</ul>

We don't want to use a formatter to filter out unwanted items, because it would have to iterate the entire collection and the ng-show directives will still create 1,000 expressions for Angular to watch.

Instead, we can just drop the ng-show directive and show/hide the <li> tags by directly changing their styles by setting, for example, display: none right in your code.

Note that this will make the code a little harder to understand for people who aren't familiar with what your application does, but remember that this is also an option. In even more extreme cases, you can write parts of your application in JavaScript outside Dart and move the root scope from the default <html> tag with ng-app.

Just don't think that when you start using Angular, absolutely everything has to be managed via Angular.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.119.103.96