On a vu qu’on pouvait établir une liaison entre une propriété dans le modèle et un élément dans la vue. Mais pour le moment on a utilisé des valeurs simples : nombre ou chaîne de caractères. Dans de multiple cas on aura besoin d’afficher des valeurs contenues dans un tableau. Comment procéder dans ce cas ? Vue.js nous propose une directive puissante et simple à utiliser pour réaliser cela.

La directive v-for

La directive v-for permet de répéter l’affichage d’un élément du DOM en fonction des valeurs contenues dans un tableau du modèle. Un petit exemple vous fera comprendre cela rapidement.

Voilà le HTML :

<ul id="tuto">
  <li v-for="value in items">
    {{ value }}
  </li>
</ul>

Le but est de créer une liste non ordonnée. On englobe donc avec une balise ul. On prévoit la directive v-for dans la balise li qui doit être répétée et on précise le nom de la propriété qui contient les objets. On utilise enfin mustache pour les valeurs des items.

Voilà le Javascript :

new Vue({
  el: '#tuto',
  data: {
    items: [
      "Je suis l'item 1",
      "Je suis l'item 2",
      "Je suis l'item 3"
    ]
  }
});

On a la propriété items qui contient un tableau de valeurs.

Avec ce rendu :

img01

Le tableau est donc parcouru et pour chaque objet rencontré une balise li est créée avec la valeur de l’item.

Voici une illustration du fonctionnement :

img12

Un index

Il est souvent utile de connaître l’index de l’élément répété. Vue.js est équipé de la variable $index. Voici un exemple :

<li v-for="value in fruits">
  {{ $index }} => {{ value }}
</li>

Avec ce Javascript :

new Vue({
  el: '#tuto',
  data: {
    fruits: [
      'pomme',
      'cerise',
      'abricot'
    ]
  }
});

Et ce résultat :

img03

Le principal intérêt de connaître l’index est dans une perspective dynamique. Voici le code HTML modifié pour illustrer cela :

<li v-for="value in fruits" v-on:click="getFruit($index)">
  {{ $index }} => {{ value }}
</li>

On a maintenant en place la détection d’un clic. On transmet comme paramètre l’index.

Voici le Javascript :

new Vue({
  el: '#tuto',
  data: {
    fruits: [
      'pomme',
      'cerise',
      'abricot'
    ]
  },
  methods: {
    getFruit: function(index) {
      alert('Je suis ' + this.fruits[index]);
    }
  }
});

On a une méthode getFruit qui attend l’argument index. Selon la valeur de l’index on va extraire le bon fruit du tableau et obtenir ce genre de message :

img04

Sans cet index on ne pourrait pas localiser l’action.

Voici une illustration de ce fonctionnement :

img13

Itérer un objet

On a vu qu’on peut créer une répétition dans le DOM avec un tableau de valeur. Peut-on aussi le faire à partir d’un tableau d’objets ? La réponse est positive et nous allons voir comment faire avec un premier exemple.

Voici la partie HTML :

<table class="table table-bordered">
  <caption>Personnes</caption>
  <thead>
    <tr>
     <th>Nom</th>
     <th>Prénom</th>
    </tr>
  </thead>
  <tbody id="tuto">
    <tr v-for="personne in personnes">
      <td>{{ personne.nom }}</td>
      <td>{{ personne.prenom }}</td>   
    </tr>         
</table>

La structure est en place et on va répéter des cellules avec les informations du modèle. On se réfère à la propriété personnes du modèle et aux propriétés nom et prenom.

Voici le Javascript :

new Vue({
  el: '#tuto',
  data: {
    personnes: [
      {nom: "Durand", prenom: "Jacques"},
      {nom: "Dupont", prenom: "Albert"},
      {nom: "Martin", prenom: "Denis"},
    ]
  }
});

On a le modèle avec toutes les personnes mémorisées (nom et prénom).

Voici le résultat à l’affichage :

img06

Voici une schématisation du fonctionnement :

img14

Et quand le tableau est modifié ?

On a vu que lorsqu’on lie une propriété avec une valeur simple le changement de celle-ci est répercuté dans la vue. Qu’en est-il avec les tableaux ?

Les mutateurs

Toutes les méthodes qui modifient un tableau (les mutateurs) sont prises en compte. Voici un exemple avec les mutateurs les plus courants :

On conserve le HTML utilisé ci-dessus :

<table class="table table-bordered">
  <caption>Personnes</caption>
  <thead>
    <tr>
     <th>Nom</th>
     <th>Prénom</th>
    </tr>
  </thead>
  <tbody id="tuto">
    <tr v-for="personne in personnes">
      <td>{{ personne.nom }}</td>
      <td>{{ personne.prenom }}</td>   
    </tr>  
  </tbody>       
</table>

Et voici le Javascript :

var vm = new Vue({
  el: '#tuto',
  data: {
    personnes: [
      {nom: "Durand", prenom: "Jacques"},
      {nom: "Dupont", prenom: "Albert"},
      {nom: "Martin", prenom: "Denis"},
    ]
  }
});

setTimeout(function(){vm.personnes.push({nom: "Claret", prenom: "Marcel"});}, 2000);
setTimeout(function(){vm.personnes.pop();}, 4000);
setTimeout(function(){vm.personnes.unshift({nom: "Claret", prenom: "Marcel"});}, 6000);
setTimeout(function(){vm.personnes.shift();}, 8000);
setTimeout(function(){vm.personnes.splice(1, 1, {nom: "Claret", prenom: "Marcel"});}, 10000);
setTimeout(function(){vm.personnes.sort(function (a, b) { return (a.nom > b.nom) });}, 12000);

Lorsque vous chargez la page le tableau apparaît comme vu ci-dessus, puis toutes les 2 secondes il se transforme conformément aux méthodes utilisées. Vous allez constater que le tableau de la vue suit fidèlement les changements du tableau du modèle.

Les accesseurs

Les accesseurs ne modifient pas un tableau mais en retournent un nouveau. Dans ce cas évidemment les modifications ne sont pas répercutées. Il faut donc dans ce cas remplacer l’ancien tableau par le nouveau. Prenons à nouveau un exemple. le HTML est le même que ci-dessus.

Voici la partie Javascript :

var vm = new Vue({
  el: '#tuto',
  data: {
    personnes: [
      {nom: "Durand", prenom: "Jacques"},
      {nom: "Dupont", prenom: "Albert"},
      {nom: "Martin", prenom: "Denis"},
    ]
  }
});

setTimeout(function(){
  vm.personnes = vm.personnes.concat([
    {nom: "Claret", prenom: "Marcel"},
    {nom: "Verlou", prenom: "Gustave"}
  ]);
}, 2000);
setTimeout(function(){vm.personnes = vm.personnes.slice(1, 4);}, 4000);

On a à nouveau une modification du tableau toutes les 2 secondes. Vous remarquerez que cette fois il a fallu affecter à nouveau le tableau pour prendre en compte les modifications.

Les méthodes augmentées

Regardez maintenant cet exemple :

var vm = new Vue({
  el: '#tuto',
  data: {
    personnes: [
      {nom: "Durand", prenom: "Jacques"},
      {nom: "Dupont", prenom: "Albert"},
      {nom: "Martin", prenom: "Denis"},
    ]
  }
});

vm.personnes[1] = {nom: "Claret", prenom: "Marcel"};

Dans ce cas le tableau ne sera pas mis à jour dans la vue.

Pour réaliser cela vue.js propose la méthode augmentée $set :

var vm = new Vue({
  el: '#tuto',
  data: {
    personnes: [
      {nom: "Durand", prenom: "Jacques"},
      {nom: "Dupont", prenom: "Albert"},
      {nom: "Martin", prenom: "Denis"},
    ]
  }
});

vm.personnes.$set(1, {nom: "Claret", prenom: "Marcel"});

Cette fois le nouvel objet sera pris en compte !

Un exemple

Avec tout ce que nous avons vu jusqu’à présent nous allons pouvoir élaborer un exemple un peu plus intéressant. Disons que nous avons une liste de personnes dans un tableau. Nous voulons pour chaque personne un bouton pour le mettre à la poubelle. On a donc aussi une liste de personnes en poubelle. Dans cette poubelle nous voulons pouvoir soit rétablir la personne dans la liste des personnes, soit la supprimer complètement. Si ce n’est pas très clair raconté comme ça ce sera sans doute mieux en faisant fonctionner l’exemple.

Voici le code complet :

<!DOCTYPE html>
<html lang="fr">

  <head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">
    <title>Test vue.js</title>
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">
  </head>

  <body>

    <div class="container" id="tuto">
      <br>

      <div class="panel panel-primary" v-show="personnes.length">
        <div class="panel-heading">Personnes actives</div>        
        <table class="table table-bordered table-striped">
          <thead>
            <tr>
             <th class="col-sm-5">Nom</th>
             <th class="col-sm-5">Prénom</th>
             <th class="col-sm-2"></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="personne in personnes">
              <td>{{ personne.nom }}</td>
              <td>{{ personne.prenom }}</td> 
              <td><button class="btn btn-warning btn-block" v-on:click="supprimer($index)">Poubelle</button></td>  
            </tr>  
          </tbody>       
        </table>
        <div class="panel-footer">
          &nbsp
          <button class="button btn btn-xs btn-warning" v-on:click="toutPoubelle">Tout à la poubelle</button>
        </div>
      </div> 

      <div class="panel panel-danger" v-show="poubelle.length">
        <div class="panel-heading">Poubelle</div>
        <table class="table table-bordered table-striped">
          <thead>
            <tr>
             <th class="col-sm-4">Nom</th>
             <th class="col-sm-4">Prénom</th>
             <th class="col-sm-2"></th>
             <th class="col-sm-2"></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="personne in poubelle">
              <td>{{ personne.nom }}</td>
              <td>{{ personne.prenom }}</td> 
              <td><button class="btn btn-success btn-block" v-on:click="retablir($index)">Rétablir</button></td>
              <td><button class="btn btn-danger btn-block" v-on:click="eliminer($index)">Supprimer</button></td>    
            </tr>  
          </tbody>       
        </table>
        <div class="panel-footer">
          &nbsp
          <div class="btn-group">
            <button class="button btn btn-xs btn-success" v-on:click="toutRetablir">Tout rétablir</button>
            <button class="button btn btn-xs btn-danger" v-on:click="toutEliminer">Tout supprimer</button> 
          </div>
        </div>
      </div> 

    </div>

    <script src="http://cdn.jsdelivr.net/vue/1.0.10/vue.min.js"></script>

    <script>

      var vm = new Vue({
        el: '#tuto',
        data: {
          personnes: [
            {nom: "Claret", prenom: "Marcel"},
            {nom: "Dupont", prenom: "Albert"},
            {nom: "Durand", prenom: "Jacques"},            
            {nom: "Martin", prenom: "Denis"},
            {nom: "Torlet", prenom: "Arthur"}            
          ],
          poubelle: []
        },
        methods: {
          supprimer: function(index) {
            this.poubelle.push(this.personnes[index]);
            this.personnes.splice(index, 1);
            this.poubelle.sort(ordonner);
          },
          retablir: function(index) {
            this.personnes.push(this.poubelle[index]);
            this.poubelle.splice(index, 1);
            this.personnes.sort(ordonner);
          },
          eliminer: function(index) {
            this.poubelle.splice(index, 1);
          },
          toutPoubelle: function() {
            this.poubelle = this.poubelle.concat(this.personnes);
            this.poubelle.sort(ordonner);
            this.personnes = [];
          },
          toutRetablir: function() {
            this.personnes = this.personnes.concat(this.poubelle);
            this.personnes.sort(ordonner);
            this.poubelle = [];
          },
          toutEliminer: function() {
            this.poubelle = [];
          }
        }
      });

      var ordonner = function (a, b) { return (a.nom > b.nom) };

    </script>

  </body>

</html>

Au départ on a la liste des personnes :

img08

Pour chaque personne on dispose d’un bouton pour le mettre à la poubelle. On a aussi un bouton pour mettre tout le monde à la poubelle. Si je clique par exemple sur le bouton de Dupont j’obtiens :

img09

Le panneau de la poubelle devient visible avec Dupont présent. On dispose d’un bouton pour le rétablir et d’un autre pour le supprimer définitivement. Si on clique sur « Rétablir » on se retrouve dans la situation initiale.

Si on envoie Martin aussi à la poubelle on se retrouve ainsi :

img10

On remarque que la liste de la poubelle s’ordonne automatiquement.

Si on envoie tout le monde à la poubelle le premier panneau disparaît et il ne reste que la liste de la poubelle :

img11

On peut rétablir une personne ou toutes d’un coup. Vous avez compris le principe, je vous laisse faire des tests et analyser le code, il ne comporte que des choses que nous avons déjà vues et c’est une bonne occasion pour réviser ce qui a été présenté lors des articles précédents.

En résumé

  • La directive v-for permet de répéter des éléments du DOM à partir de données du modèle.

  • La directive v-for génère un index pour identifier les éléments.

  • On peut itérer un tableau de valeurs ou d’objets.

  • Lorsqu’on utilise un accesseur il faut assigner de nouveau le tableau généré.

  • On dispose de la méthode augmentée $set pour assigner un élément de tableau.

Laisser un commentaire