Je vous ai dit dans le précédent article que vue.js est destiné à créer des interfaces interactives. Pour le moment je ne vous l’ai pas encore prouvé ! Dans cet article on va voir comment on gère les actions de l’utilisateur.

La directive v-on

Pour intercepter les événements survenant au niveau de la vue vue.js offre la directive v-on.

Prenons un petit exemple pour introduire cette notion :

<div id="tuto">
  <a v-on:click="action">Cliquez ici !</a>
</div>

On a mis la directive v-on dans la balise a. Notez la syntaxe : on fait suivre le nom de la directive de deux points, du nom de l’événement, puis du nom d’une fonction. On comprend vite qu’on va appeler la fonction lorsqu’on va cliquer sur le lien.

Voici le code Javascript :

new Vue({
  el: '#tuto',
  methods: {
    action: function () {
      alert('On a cliqué !');
    }
  }
});

Nous découvrons une propriété pas encore rencontrée : methods. C’est ici qu’on va placer les méthodes de la VueModèle. Ici on a besoin d’une méthode action pour répondre à l’événement de clic.

Le résultat au clic est donc l’ouverture de la petite fenêtre d’information :

img01

Voici une illustration de la liaison :

img12

On commence à avoir de l’interactivité !

Il existe une syntaxe plus courte bien pratique pour la directive v-on, il suffit de la remplacer par le caractère @, on écrira ainsi par exemple @click.

On peut aussi, au lieu d’une fonction, utiliser une expression comme argument de la directive v-on. Voici un exemple :

<div id="tuto">
  <h1> 
    <button class="button btn-primary" v-on:click="++n">+</button> {{ n }}
  </h1>
</div>

Cette fois on utilise un bouton et le même événement click. Mais au lieu d’une fonction on se contente d’incrémenter la variable n. D’autre part on affiche la valeur de cette variable avec mustache. Cette approche est à réserver à des traitements sommaires.

Voici le Javascript :

new Vue({
  el: '#tuto',
  data: { n: 0 }
});

On initialise n avec la valeur 0 et on sait que sa valeur sera systématiquement répercutée dans la vue. Donc chaque fois qu’on clique sur le bouton la valeur affichée s’incrémente. Voilà l’aspect obtenu :

img03

Voici une schématisation du fonctionnement :

img13

La directive v-bind

Tous les événements disponibles avec Javascript le sont évidemment aussi avec vue.js, ce qui ouvre pas mal de perspectives au niveau de l’interactivité. Prenons un petit exemple pour un changement d’aspect au survol. Voici la vue :

<div id="tuto">
  <h1> 
    <span class="label" v-bind:class="type" v-on:mouseover="changeType">{{type}}</span>
  </h1>
</div>

On voit ici une nouvelle directive : v-bind. Elle permet de lier un attribut à une expression (mustache ne fonctionne pas pour les attributs avec la version 2). Ici on va donc pouvoir changer de façon dynamique  la valeur d’un attribut, ici une classe (ça serait la même chose pour du style).

D’autre part cette fois l’événement utilisé est mouseover, autrement dit le déclenchement va avoir lieu au survol de l’élément par le curseur de la souris.

Voici le Javascript :

new Vue({
    el: '#tuto',
    data: {
        type: 'label-primary'
    },
    methods: {
        changeType: function() {
            this.type = (this.type == 'label-primary') ? 'label-success' : 'label-primary';
        }
    }
});

On a dans les données la propriété type qui au départ a la valeur « label-primary ». A chaque survol de l’étiquette par le curseur de la souris cette valeur va changer, alternant entre « label-primary » et « label-success ». Du coup l’aspect à l’écran va aussi changer en conséquence.

D’autre part on modifie aussi le texte de l’étiquette qui est relié à la même propriété.

Donc au départ on a :

img05

Et ensuite :

img06

Et ainsi de suite à chaque survol…

Voici une illustration du fonctionnement :

img07

Il existe une syntaxe plus courte pour la directive v-bind, il suffit de la remplacer par le caractère :, on écrira ainsi par exemple :class.

Les modificateurs

Les événements

Quand on utilise des événements avec JavaScript on a fréquemment besoin d’utiliser aussi event.preventDefault() ou event.stopPropagation(). On peut évidemment ajouter ces méthodes dans le code mais il y a bien plus simple avec Vue.js !

On peut ajouter aux événements des modificateurs, il en existe 4 :

  • .stop : on stoppe la propagation de l’événement après le noeud actuel du DOM,
  • .prevent : on empêche l’action implicite d el’événement,
  • .capture : on utilise le mode capture quand on ajoute l’événement,
  • .self : la cible doit être l’élément lui-même,

Au niveau de la syntaxe c’est tout simple, par exemple :

<form @submit.prevent="onSubmit"></form>

En plus on peut chaîner les modificateurs :

<form @submit.stop.prevent="onSubmit"></form>

Les touches du clavier

Quand on écoute les touches du clavier on aime savoir quelle touche a été activée. Là aussi Vue.js nous simplifie la vie. il suffit d’ajouetr le code de la touche à l’événement :

<input @keydown.13="submit">

Pour nous simplifier encore plus la vie on dispose d’alias pour les touches les plus courantes :

  • enter
  • tab
  • delete
  • esc
  • space
  • up
  • down
  • left
  • right

Par exemple :

<input @keydown.enter="submit">

Le chronomètre

On va poursuive l’exemple du chronomètre vu lors du précédent chapitre en lui ajoutant de l’interactivité. On va en profiter au passage pour découvrir la directive v-show très utile qui permet de masquer des éléments du DOM à l’affichage. Voici la nouvelle version complète :

<!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.7/css/bootstrap.min.css">
  </head>

  <body>

    <div class="container">

      <div id="tuto" class="text-center">
        <h1>Le chronomètre</h1>
        <hr>
        <h1>
          <span class="label label-primary">{{ time.minutes }}</span> minutes
          <span class="label label-primary">{{ time.secondes }}</span> secondes
        </h1>
        <br>
        <p>
          <button class="btn btn-danger btn-lg" v-show="etat.backward" @click="backward">Effacer</button>
          <button class="btn btn-danger btn-lg" v-show="etat.stop" @click="stop">Arrêter</button>
          <button class="btn btn-primary btn-lg" v-show="etat.play" @click="play">{{ btnPlay }}</button>
        </p>
      </div>

    </div>

    <script src="https://unpkg.com/vue@2.0.3/dist/vue.js"></script>

    <script>

      var vm = new Vue({
        el: '#tuto',
        data: {
          time: {
            minutes: 0,
            secondes: 0
          },
          btnPlay: 'Démarrer',
          etat: {
            stop: false,
            backward: false,
            play: true
          }
        },
        methods: {
          backward: function() {
            chronoReset();
          },
          play: function() {
            chronoStart();            
          },
          stop: function() {
            chronoStop();
          }
        }
      });

      var totalSecondes = 0;
      var timer;

      chronoStart = function() {
        timer = setInterval(function() {
          vm.time.minutes = Math.floor(++totalSecondes / 60);
          vm.time.secondes = totalSecondes - vm.time.minutes * 60;
        }, 1000);
        setEtat(false, true, false);
        vm.btnPlay = 'Continuer';
      };

      chronoStop = function() {
        clearInterval(timer);
        setEtat(true, false, true);
      };

      chronoReset = function() {
        totalSecondes = 0;
        vm.time.minutes = 0;
        vm.time.secondes = 0;
        setEtat(true, false, false);
        vm.btnPlay = 'Démarrer';        
      }

      setEtat = function(play, stop, backward) {
        vm.etat.play = play;
        vm.etat.stop = stop;
        vm.etat.backward = backward;        
      };

    </script>

  </body>

</html>

La vue

Voici la partie HTML :

<div id="tuto" class="text-center">
  <h1>Le chronomètre</h1>
  <hr>
  <h1>
    <span class="label label-primary">{{ time.minutes }}</span> minutes
    <span class="label label-primary">{{ time.secondes }}</span> secondes
  </h1>
  <br>
  <p>
    <button class="btn btn-danger btn-lg" v-show="etat.backward" @click="backward">Effacer</button>
    <button class="btn btn-danger btn-lg" v-show="etat.stop" @click="stop">Arrêter</button>
    <button class="btn btn-primary btn-lg" v-show="etat.play" @click="play">{{ btnPlay }}</button>
  </p>
</div>

Pour simplifier je n’ai gardé que les minutes et secondes, ce qui est réaliste pour un chronomètre. On utilise 2 étiquettes avec une liaison « mustache ». On trouve également 3 boutons de commandes avec pour chacun :

  1. une directive v-on (avec la syntaxe courte @) pour détecter l’événement click

  2. un directive v-show pour pouvoir changer la visibilité du bouton

D’autre part le troisième bouton a son texte avec une liaison « mustache », il pourra donc changer de nom.

Le Javascript

Voyons la partie Javascript. D’abord la déclaration propre à vue.js :

var vm = new Vue({
  el: '#tuto',
  data: {
    time: {
      minutes: 0,
      secondes: 0
    },
    btnPlay: 'Démarrer',
    etat: {
      stop: false,
      backward: false,
      play: true
    }
  },
  methods: {
    backward: function() {
      chronoReset();
    },
    play: function() {
      chronoStart();            
    },
    stop: function() {
      chronoStop();
    }
  }
});

On trouve :

  • la liaison avec l’élément du DOM : #tuto

  • le modèle (data) avec 3 propriétés :

  1. time : 2 propriétés pour les minutes et secondes du chronomètre

  2. btnPlay : pour le texte du bouton (Démarrer/Continuer)

  3. etat : 3 propriétés pour la visibilité des 3 boutons

  • les méthodes :

  1. backward : pour remettre le chronomètre à 0

  2. play : pour lancer ou relancer le chronomètre

  3. stop : pour arrêter le chronomètre

On a ensuite les variables globales et les fonctions de gestion :

var totalSecondes = 0;
var timer;

chronoStart = function() {
  timer = setInterval(function() {
    vm.time.minutes = Math.floor(++totalSecondes / 60);
    vm.time.secondes = totalSecondes - vm.time.minutes * 60;
  }, 1000);
  setEtat(false, true, false);
  vm.btnPlay = 'Continuer';
};

chronoStop = function() {
  clearInterval(timer);
  setEtat(true, false, true);
};

chronoReset = function() {
  totalSecondes = 0;
  vm.time.minutes = 0;
  vm.time.secondes = 0;
  setEtat(true, false, false);
  vm.btnPlay = 'Démarrer';        
}

setEtat = function(play, stop, backward) {
  vm.etat.play = play;
  vm.etat.stop = stop;
  vm.etat.backward = backward;        
};

Le fonctionnement

Au départ on a cet aspect :

img08

Lorsqu’on clique sur le bouton « Démarrer » le chronomètre se met en marche. Au niveau des boutons « Démarrer » disparaît et « Arrêter » apparaît :

img09

Lorsqu’on clique sur « Arrêter » le comptage s’interrompt, le bouton « Arrêter » disparaît. Les boutons « Effacer » et « Continuer » apparaissent :

img10

Si on clique sur « Effacer » on revient à la situation de départ et si on clique sur « Continuer » le comptage reprend et le bouton « Arrêter » réapparaît.

En résumé

  • On gère les événements avec la directive v-on. La syntaxe courte est @ .

  • On peut ajouter des modificateurs aux événements.

  • On modifie des attributs avec la directive v-bind. La syntaxe courte est : .

  • On masque les éléments du DOM avec la directive v-show .

Laisser un commentaire