It is not very common to see a template system employing live DOM and HTML syntax but this approach turns out to work surprisingly well in practice. People used to other, string-based template engines might need some time to adjust, but after a few initial hops writing DOM-based templates becomes a second nature. There are just a couple of caveats.

Living with verbose syntax

Firstly, the syntax for some of the constructs might be a bit verbose. The best example of this slightly annoying syntax for the ng-switch family of directives some common use cases might simply require a lot of typing. Let’s consider a simple example of displaying a different message based on a number of items in a list:

<div ng-switch on="items.length>0">
  <span ng-switch-when="true">
    There are {{items.length}} items in the list.
  <span ng-switch-when="false">
    There are no items in the list.

Of course it would be possible to move the message-preparing logic to a controller to avoid switch statements in a template, but it feels like this type of simple conditional logic has its place in the view layer.

Fortunately, the latest version of angularjs: the ng-if directive built-in is a very handy tool for cases where you don’t need power of a full if/else expression.

ngRepeat and multiple DOM elements

A slightly more serious issue is connected with the fact that, in its simplest form, the ng-repeat repeater only knows how to repeat one element (with its children). This means that ng-repeat can’t manage a group of sibling elements.

To illustrate this let’s imagine that we’ve got a list of items with a name and description. Now, we would like to have a table where both the name and the description are rendered as separate rows (<tr>). The problem is that we need to add the <tbody> tag just to have a place for the ng-repeat directive:

  <tbody ng-repeat="item in items">

It looks like the ng-repeat directive needs a container element and forces us to create a certain HTML structure. This might or might not be a problem depending on how strictly you need to follow markup structure outlined by your web designers.

In the previous example we were lucky since an appropriate HTML container exists (<tbody>), but there are cases where there is no valid HTML element where the ng-repeat directive could be placed. We could think of another example where we would like to have an HTML output like:

  <!-- we would like to put a repeater here -->
  <!-- and end it here -->

A new version of angularjs (1.2.x) is going to extend the basic syntax of the ngRepeat directive to allow more flexibility in choosing DOM elements to be iterated over. In the future it will be possible to write:

  <li ng-repeat-start="item in items">
  <li ng-repeat-end>{{item.description}}</li>

By using the ng-repeat-start and the ng-repeat-end attributes it will be possible to indicate a group of sibling DOM elements to be iterated over.

Elements and attributes that can’t be modified at runtime

Since angularjs operates on the live DOM tree in a browser it can only do as much as browsers permit. It turns out that there are some corner cases where some browsers disallow any changes to elements and their attributes, after those elements were put in the DOM tree.

To see the consequences of those restrictions in practice let’s consider a rather common use case of specifying an input element’s type attribute dynamically. Many users have tried (and failed!) to write code along those lines:

<input type="{{myinput.type}}" ng-model="myobject[myinput.model]">

The trouble is that some browsers (yes! you’ve guessed it: Internet Explorer!) won’t change type of a created input. Those browsers see {{myinput.type}} (un-evaluated) as an input type, and since it is unknown it is interpreted as type="text".

There are several approaches to tackling the problem described earlier, but we need to learn more about angularjs custom directives before we can discuss those solutions.

<ng-include src="'input'+myinput.type+'.html'"></ng-include>

Where the included fragment specifies input’s type as static string.


Pay attention to scoping issues when using this technique as ng-include creates a new scope.