Angular JS Directives – Repeating over multiple elements

I was watching a video from NDC featuring a cage match between Ember JS and Angular JS. Tom Dale and Rob Connery both did a good job of representing Ember and Angular respectively. At one point, Tom showed a feature of handlebars (which Ember uses for templating) that allowed him to iterate over a javascript array and for each element render out two html TR (table row) tags. This is easy to do in Ember because handlerbars uses a #each template and you can basically put anything you want between #each and /each like so (tom’s code from the talk follows):

{{#each orders}}
  <tr>
    <td>{{description}}</td>
  </tr>
 
  <tr>
    <td>{{number}}</td>
    <td>{{currency price}}</td>
  </tr>
{{/each}}

The result is something that looks like this (screenshot from the video):

Ember JS each example

So for each item, show the description on one line and the number and price (formatted as currency) on the next line. Pretty standard stuff for simple tabular data.

When it came time for Rob to offer rebuttal and duplicate this with Angular, he struggled and I don’t blame him. Even though I’ve done a bit of Angular and given presentations on the topic, while watching this I was stumped. You see Angular has an ng-repeat directive that allows you to iterate over a collection, much like #each. However, ng-repeat is applied to a DOM element and that element itself is used as the template for every item. To illustrate, check out this simple ng-repeat example:

<tr ng-repeat="order in orders">
  <td>{{order.description}}</td>
  <td>{{order.number}}</td>
  <td>{{order.price}}</td>
</tr>

Here we ask angular to iterate over all “orders” and for each iteration place the current item in a variable called “order”. The TR tag itself will be used as a template and anything inside of it will be part of that template also. The problem now is how do you extend this to multiple TR tags like Tom did in the Ember example? It’s not something I had seen before. So I decided to do some research and see if there was in fact a way to do this in Angular.

I came across a few posts that talked about adding the ability to use ng-repeat in an html comment (similar to how Knockout JS bindings can be used in comments), but the Angular lead turned that proposal down. I then found this page on Angular’s Git hub Page. It talks about a new feature of Angular, Directive Virtual Groups. This exactly what I needed ! 🙂

So how does it work? In a nutshell it’s a way to have a directive span over multiple elements by indicating a start and end. This is done by simply appending -start and -end to the directive itself. It’s actually a very nice way of doing this as it extends existing directives.

Unfortunately this isn’t the stable release of Angular, but it’s good to know that this feature is in the code base and is coming. So to play around with it I had to download the  Angular source code and build it on my machine. After that I used the newly built angular.min.js file in a sample application and I was able to write code like this:

 <tr ng-repeat-start="order in orders">
    <td>{{order.description}}</td>
  </tr>
  <tr ng-repeat-end>
    <td>{{order.number}}</td>
    <td>{{order.price}}</td>
  </tr>

This new sytax allows us to take the repeat binding and extend it over two html elements, or any number of elements between -start and -end. If I included a 3rd row in between those TR’s, it would rendered as well.

I think this is a great addition to the framework and I love how it’s implemented as an extension to directives and really highlights the good architecture and separation of concerns you can get. Directives are complicated, but what they do offer is a clean way to isolate functionality and to build reusable components that can be composed on a page, giving the application developer greater power and flexibility

8 thoughts on “Angular JS Directives – Repeating over multiple elements

    • Thanks for the comment!

      The problem with having ng-repeat on the tbody tag is that it will render tbody multiple times for each row, that’s not what we want since it’s not semantically correct and could mess up our formatting.

      ng-repeat doesn’t just use anything inside an html tag as the template, but also the tag itself.

      Take a look at this fiddle:http://jsfiddle.net/SeJvH/
      If you inspect the results window you’ll see a tbody tag for each row.

      Here’s what you get when looking at the output in Dev Tools:
      http://wp.me/a1tfnZ-5S

  1. Hmm interesting. Well good to know, but it still doesn’t address the issue where I may not want a table body for each row. Or imagine if this was UL tag and I wanted multiple Li tags for each item in the collection, I wouldn’t want multiple UL tags.

    I guess maybe a table isn’t the best example, but it was part of the challenge mentioned in the original video . I’m just trying to highlight how you can have multiple dom elements as the template for ng-repeat without having to place the directive on a parent element.

Leave a reply to Hattan Cancel reply