Imbriquer des ngRepeat avec Angular

Angular c'est magique mais parfois c'est un peu compliqué. Ça le devient par exemple avec la directive ngRepeat qui permet de faire des boucles. Par exemple, pour lister un tableau sous forme de liste, nous ferrons :

<ul>
    <li ng-repeat="text in list">
        {{text}}
    </li>
</ul>

C'est l'élément qui contient la directive ng-repeat qui sera répétée pour chaque élément du tableau.

Là où ça devient compliqué, c'est lorsqu'on veut répéter un ensemble d'éléments pour chaque item :

<table>
    <tr ng-repeat="item in list">
        <td>
            {{item.title}}
        </td>
        <td>
            {{item.price}}
        </td>
    </tr>
</table>

Non ça va, c'est encore facile ça. Mais si on veut répéter des éléments frères comme dans une liste de définitions, ça devient plus compliqué :

<dl>
    <div ng-repeat="item in list">
        <dt>{{item.title}}</dt>
        <dd>{{item.def}}</dd>
    </div>
</dl>

Là je suis obligé de mettre un div dans mon dl, ce qui est strictement interdit par le W3C. Alors je fais comment ? J'utilise la directive ng-repeat-start et end de la sorte :

<dl>
    <dt ng-repeat-start="item in list">{{item.title}}</dt>
    <dd ng-repeat-end>{{item.def}}</dd>
</dl>

Ainsi, la boucle va commencer sur l'élément dt et se terminer sur l'élément frère dd. Je pourrais aussi avoir ça par exemple :

<div ng-repeat-start="item in list">{{item.title}}</div>
<div>{{item.desc}}</div>
<div>{{item.lol}}</div>
<div ng-repeat-end>{{item.price}}</div>

…afin d'avoir 4 divs pour chaque élémens sans pour autant les regrouper dans un parent.

Et on en arrive à l'exemple qui m'a emmené à cette réflexion : une collection de listes d'objets dont je veux afficher le titre, puis les éléments sans les imbriquer, en gros on arrivera à ça :

<table>
    <tr>
        <th>Catégorie n°1</th>
    </tr>
    <tr>
        <td>Item n°1</td>
    </tr>
    <tr>
        <td>Item n°2</td>
    </tr>
    <tr>
        <th>Catégorie n°2</th>
    </tr>
    <tr>
        <td>etc.</td>
    </tr>
</table>

…à partir d'un objet de cette forme :

{
    "catégorie n°1": [{
        "title": "Item n°1"
    }, {
        "title": "Item n°2"
    }, {
        "title": "Item n°3"
    }],
    "catégorie n°2": [{
        "title": "etc."
    }]
}

On peut y arriver grâce à ng-repeat-start de la sorte :

<table>
    <tr ng-repeat-start="(title, items) in list">
        <th>{{title}}</th>
    </tr>
    <tr ng-repeat-end
        ng-repeat="item in items">
        <td>{{item.title}}</td>
    </tr>
</table>

Le second élément est à la fois un point d'arret pour la première boucle mais aussi un démarrage d'une nouvelle boucle basée sur une variable produite par la première boucle. C'est beau.

Hadrien

Hi, I'm a french Javascript Lead Developer, Web Architect from Toulouse, France. I've worked for 12 years for many projects with YUI, AngularJS, Aurelia.io and now React and React native.

Toulouse, France https://hadrien.eu