Using ng-repeat-start in AngularJS

You are probably familiar with the use of ng-repeat to repeat the DOM element it is attached to. One common use is rendering table columns using ng-repeat. The limitation of this approach is that ng-repeat will only repeat the element it is attached to, such as the <td> tag. As of AngularJS 1.2 you can define repeat start and end points using ng-repeat-start and ng-repeat-end. This means you can repeat a number of elements instead of just the first one.

Using ng-repeat

Let’s take a look at an example of ng-repeat to render a table. Suppose we have array of objects metaData which describes our table data. A second array of objects data contains the table data itself.

$scope.metaData = [
    {'label': 'Name', 'field': 'name'},
    {'label': 'Age', 'field': 'age'}
];
$scope.data = [
    {'name': 'Sam', 'age': 23},
    {'name': 'James', 'age': 36},
    {'name': 'Sarah', 'age': 29}
];

A typical approach to render this in a table using ng-repeat would be this:

  • For each entry in metaData, render a <th> element
  • For each entry in tableData, render a <tr> element
  • Within each <tr> element, render a column for each entry in metaData

The HTML would look like this:

<table class="table table-striped">
  <thead class="thead-light">
    <tr>
      <th ng-repeat="meta in metaData">{{meta.label}}</th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="row in tableData">
      <td ng-repeat="meta in metaData">{{row[meta.field]}}</td>
    </tr>
  </tbody>
</table>

And it will be rendered like this (with some Bootstrap styling):

ng-repeat table example

This is a perfectly valid approach. But what if you needed to render additional columns using <td> without altering the data arrays? You can’t do it easily with ng-repeat (you could create a custom directive, but let’s not go there).

Using ng-repeat-start

In AngularJS 1.2 you can use ng-repeat-start and ng-repeat-end. I’ll explain it’s usage by building on the previous example. Say you wanted an additional column to render a button for each row after the “Age” column.

  • The first <th> and <td> elements now use ng-repeat-start instead of ng-repeat, otherwise they remain unchanged.
  • A second <th> element is added to conditionally render an additional table header. This also marks the end of the first ng-repeat with ng-repeat-end.
  • A second <td> element is added to conditionally render the button column. This also marks the end of the second ng-repeat with ng-repeat-end.
<table class="table table-striped">
  <thead class="thead-light">
    <tr>
      <th ng-repeat-start="meta in metaData">{{meta.label}}</th>
      <th ng-repeat-end ng-if="meta.field == 'age'">Controls</th>
    </tr>
  </thead>
  <tbody>
    <tr ng-repeat="row in tableData">
      <td ng-repeat-start="meta in metaData">{{row[meta.field]}}</td>
      <td ng-repeat-end ng-if="meta.field == 'age'">
        <button type="button" class="btn btn-success mr-2">Modify age</button>
      </td>
    </tr>
  </tbody>
</table>

This has the effect of expanding the elements repeated by ng-repeat. Note that the element with ng-repeat-end is included in the repeating.

Now our table is rendered like this:

ng-repeat-start table example

Further reading

The AngularJS documentation provides a detailed explanation of ngRepeat, including a section dedicated to ng-repeat-start called Special repeat start and end points: https://docs.angularjs.org/api/ng/directive/ngRepeat