Tutoriel pour apprendre à utiliser le framework Vue.js version 1

  Les bases

Vue.js est une bibliothèque JavaScript qui permet de créer des interfaces web interactives. Il existe de très nombreuses bibliothèques qui proposent la même chose dont la plus célèbre est AngularJS. Pour avoir une idée de la richesse de l'offre, vous pouvez jeter un coup d'œil  sur cette page. Alors, étant donné cette prolifération pourquoi utiliser particulièrement vue.js ? À travers ce tutoriel, nous allons apprendre les bases de ce framework.

5 commentaires Donner une note à l'article (5)

Article lu   fois.

L'auteur

Liens sociaux

Viadeo Twitter Facebook Share on Google+   

I. Le JavaScript que vous devez connaître

Si vous avez déjà utilisé AngularJS vous devez connaître la difficulté de prise en main qui est nécessaire, mais on a à disposition une grande richesse fonctionnelle. C'est le prix à payer. Sur l'autre versant, on a des bibliothèques beaucoup plus simples d'accès, mais qui ont finalement peu de choses à offrir. Vue.js se situe à la confluence de ces deux mondes en alliant avec efficacité simplicité et richesse. D'autre part il est très souple et n'impose pas un certain type d'organisation.

Je vous propose donc de découvrir cette bibliothèque. Dans la première partie de cette série de tutoriels, nous verrons l'essentiel de ce que la bibliothèque nous offre de base, et vous verrez qu'il y a déjà profusion de possibilités. Dans la deuxième partie, nous verrons comment enrichir la bibliothèque selon nos besoins. Cette série sera loin d'épuiser le sujet, mais il vous mettra le pied à l'étrier et, je l'espère, vous donnera envie d'utiliser cette bibliothèque très prometteuse.

La première chose que nous allons faire est un peu de JavaScript. Si vous maîtrisez déjà ce langage, vous pouvez sauter cet article et passer directement au suivant et vous aurez toujours la possibilité d'y revenir en cas de besoin. Si ce n'est pas le cas, prenez le temps de lire. Vous n'avez pas besoin d'être un gourou de JavaScript pour utiliser vue.js, mais vous devez en maîtriser les bases.

I-A. JavaScript ?

JavaScript est un langage dynamique orienté objet à prototype, ce qui signifie qu'il n'y a pas de classe. Il est essentiellement utilisé côté client pour offrir de l'interactivité, mais il est aussi de plus en plus utilisé côté serveur, par exemple avec node.js.

Lorsqu'on compare JavaScript à d'autres langages comme C# ou Java, la première impression est celle de la simplicité. Il est vrai que ce langage se laisse facilement aborder et ne pose pas trop de problèmes à condition de ne pas trop le pousser dans ses retranchements, ce que nous ne ferons pas dans cet article. En effet JavaScript est loin d'être aussi simple qu'on peut le penser.

I-A-1. Inclusion de JavaScript

Comment est-ce qu'on inclut du code JavaScript dans une page HTML ? On peut le faire de deux façons.

I-A-1-a. Inclusion en ligne

La façon la plus simple pour inclure du JavaScript est d'ajouter le code directement dans la page avec la balise <script>. Voici un exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>alert("Coucou !")</script>
    </body>
</html>

La méthode alert permet d'afficher une petite fenêtre avec un texte. Lorsque cette page se charge, il doit apparaître ceci :

Image non disponible

Mais ce n'est pas le meilleur endroit pour placer du JavaScript. Il est plutôt recommandé d'adopter la seconde méthode que nous allons voir à présent.

I-A-1-b. Inclusion de la référence

Considérons le même cas que précédemment en changeant un peu le code de la page :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
        <script src="coucou.js"></script>
    </head>
    <body></body>
</html>

Cette fois dans la balise script on référencie le fichier coucou.js situé dans le même dossier. Voici le code de ce fichier :

 
Sélectionnez
alert("Coucou !");

Le fonctionnement sera exactement le même, mais cette fois on a bien séparé le HTML et le JavaScript.

Pour la clarté du code dans ces tutoriels, je serai amené à utiliser la première méthode.

I-A-2. Les instructions

Fondamentalement un script JavaScript est constitué d'une suite d'instructions :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var a = 2;
        var b = 3;
        document.write(a + b);
    </script>
    </body>
</html>

La partie JavaScript est constituée de trois expressions :

  1. On initialise la variable « a » et on lui affecte la valeur 2 ;
  2. On initialise la variable « a » et on lui affecte la valeur 3 ;
  3. On demande l'affichage de la somme « a + b ».

Si vous ouvrez cette page vous verrez s'afficher 5.

Le point-virgule final des instructions n'est pas obligatoire, mais vivement conseillé pour la clarté du code.

Évidemment JavaScript dispose d'instructions conditionnelles qui permettent une exécution plus souple qu'une suite d'instructions figées comme nous le verrons bientôt !

I-A-3. Les commentaires

Il est très utile de commenter le code. Bien souvent on se comprend lorsqu'on l'écrit, mais quelques mois plus tard lorsqu'on y revient on ne retrouve plus la logique qui le sous-tend. C'est encore pire lorsqu'une autre personne doit intervenir dessus. Donc commenter son code est une saine disposition qui devrait être systématique. JavaScript propose les deux types classiques de commentaires comme utilisés pour C++, PHP et d'autres grands classiques :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        /*Le code suivant est destiné à illustrer
        l'utilisation des instructions en JavaScript */
        var a = 2;
        var b = 3;
        // Ici on envoie à l'affichage le résultat de la somme des deux variables 
        document.write(a + b);
    </script>
    </body>
</html>

La première syntaxe permet d'englober plusieurs lignes alors que la seconde ne concerne que le texte disposé sur la même ligne.

I-B. Types et variables

I-B-1. Les variables

Les variables sont en quelque sorte des boîtes dans lesquelles on place des valeurs et dans lesquelles on peut ensuite les retrouver. On a vu l'utilisation de deux variables dans le code précédent avec ces deux instructions :

 
Sélectionnez
var a = 2;
var b = 3;

On utilise le mot clé var et un identifiant (le nom de la variable).

Dans le code précédent, on a groupé dans la même instruction la déclaration et l'affectation des variables. On peut le faire en deux étapes :

 
Sélectionnez
1.
2.
3.
4.
5.
// Déclaration
var a, b;
// Assignation
a = 2;
b = 3;

Une variable a une certaine portée. Lorsqu'on la déclare en dehors d'une fonction, elle est globale, donc utilisable partout. Lorsqu'on la déclare dans une fonction elle devient locale à cette fonction, donc inconnue en dehors de la fonction.

I-B-2. Les types

Les variables peuvent contenir plusieurs types de valeurs : nombre entier, nombre à virgule, chaîne de caractères… JavaScript est faiblement typé, autrement dit on peut mettre ce que l'on veut dans une variable, comme avec PHP. JavaScript va donc utiliser un de ses types internes selon la valeur d'affectation. Cela offre de la souplesse, mais il faut rester vigilant pour éviter les surprises !

I-B-2-a. Le type booléen (boolean)

C'est le type le plus simple puisqu'on a seulement deux valeurs possibles : true ou false. Voici un exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var a = true;
        var b = false;
        document.write('La variable "a" est du type ' + typeof(a) + ' et a pour valeur ' + a + '<br>');
        document.write('La variable "b" est du type ' + typeof(b) + ' et a pour valeur ' + b);
    </script>
    </body>
</html>

Le résultat est le suivant :

La variable « a » est du type boolean et a pour valeur true
La variable « b » est du type boolean et a pour valeur false

On a déclaré deux variables en leur affectant respectivement les valeurs true et false. La méthode typeof de JavaScript permet de connaître le type de la variable. On voit que JavaScript a bien déterminé le type booléen.

I-B-2-b. Le type chaîne de caractères (string)

Une chaîne de caractères est tout simplement… une suite de caractères quelconques. Regardez le code suivant :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var nom = "Dupont";
        var prenom = 'Pierre';
        document.write('La variable "nom" est du type ' + typeof(nom) + ' et a pour valeur ' + nom + '<br>');
        document.write('La variable "prenom" est du type ' + typeof(prenom) + ' et a pour valeur ' + prenom);
    </script>
    </body>
</html>

Avec ce résultat :

La variable « nom » est du type string et a pour valeur Dupont
La variable « prenom » est du type string et a pour valeur Pierre

On voit qu'on peut utiliser indifféremment des guillemets ou des apostrophes. Notez aussi au passage qu'on ajoute (concaténation) deux chaînes avec l'opérateur « + ».

I-B-2-c. Le type nombre (number)

On a déjà vu ci-dessus l'affectation de nombres. Regardez ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var a = 28;
        var b = 12.99;
        document.write('La variable "a" est du type ' + typeof(a) + ' et a pour valeur ' + a + '<br>');
        document.write('La variable "b" est du type ' + typeof(b) + ' et a pour valeur ' + b + '<br>');
        document.write('La somme "a + b" est ' + (a + b));
    </script>
    </body>
</html>

Avec ce résultat :

La variable « a » est du type number et a pour valeur 28
La variable « b » est du type number et a pour valeur 12.99
La somme « a + b » est 40.99

I-B-2-d. Null et undefined

Il nous reste à voir deux cas importants. regardez ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var a;
        var b = null;
        document.write('La variable "a" est du type ' + typeof(a) + ' et a pour valeur ' + a + '<br>');
        document.write('La variable "b" est du type ' + typeof(b) + ' et a pour valeur ' + b + '<br>');
    </script>
    </body>
</html>

Avec ce résultat :

La variable « a » est du type undefined et a pour valeur undefined
La variable « b » est du type object et a pour valeur null

Le type undefined est simple, la variable est déclarée, mais ne contient aucune valeur.

Pour ce qui concerne la deuxième variable, on voit que son type est object, nous verrons bientôt ce type un peu particulier. Sa valeur est null, ce qui signifie en fait que l'on ne veut pas qu'elle ait une valeur.

I-C. Les opérateurs

JavaScript possède tous les opérateurs classiques.

I-C-1. Arithmétique

Voilà un peu d'arithmétique :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        document.write('5 + 6 = ' + (5 + 6) + '<br>');
        document.write('5 - 6 = ' + (5 - 6) + '<br>');
        document.write('5 * 6 = ' + (5 * 6) + '<br>');
        document.write('5 / 6 = ' + (5 / 6) + '<br>');
        document.write('5 % 5 = ' + (5 % 5) + '<br>');
        document.write('6 % 5 = ' + (6 % 5) + '<br>');
    </script>
    </body>
</html>

Avec ce résultat :

5 + 6 = 11
5 - 6 = -1
5 * 6 = 30
5 / 6 = 0.8333333333333334
5 % 5 = 0
6 % 5 = 1

La seule difficulté réside dans l'opérateur « % » qui est tout simplement le reste de la division.

I-C-2. Comparaisons

Passons aux comparaisons :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        document.write('5 > 6 = ' + (5 > 6) + '<br>');
        document.write('5 < 6 = ' + (5 < 6) + '<br>');
        document.write('5 >= 5 = ' + (5 >= 5) + '<br>');
        document.write('5 <= 5 = ' + (5 <= 5) + '<br>');
        document.write('5 == 5 = ' + (5 == 5) + '<br>');
        document.write('5 == 6 = ' + (5 == 6) + '<br>');
        document.write('6 != 5 = ' + (6 != 5) + '<br>');
        document.write('6 != 6 = ' + (6 != 6) + '<br>');
    </script>
    </body>
</html>

Avec ce résultat :

5 > 6 = false
5 < 6 = true
5 >= 5 = true
5 <= 5 = true
5 == 5 = true
5 == 6 = false
6 != 5 = true
6 != 6 = false

Rien de bien compliqué là-dedans. Voyons un cas un peu plus délicat :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var a = 5;
        var b = '5';
        document.write('a == b = ' + (a == b) + '<br>');
        document.write('a === b = ' + (a === b) + '<br>');
        document.write('a != b = ' + (a != b) + '<br>');
        document.write('a !== b = ' + (a !== b) + '<br>');
    </script>
    </body>
</html>

Avec ce résultat :

a == b = true
a === b = false
a != b = false
a !== b = true

L'opérateur triple (égalité stricte ou inégalité stricte)) vérifie le type en plus de la valeur, ce que ne fait pas le double opérateur. Il faut se méfier de ces comparaisons et utiliser les opérateurs « === » et « !== » toutes les fois où c'est possible.

I-C-3. Post et préincrémentation

Considérez ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var a = 5;
        document.write('a = 5' + '<br>');
        document.write('++a = ' + ++a + '<br>');
        a = 5;
        document.write('--a = ' + --a + '<br>');
        a = 5;
        document.write('a++ = ' + a++ + '<br>');
        a = 5;
        document.write('a-- = ' + a-- + '<br>');
        a = 5;
        document.write('4 + ++a = ' + (4 + ++a) + '<br>');
        a = 5;
        document.write('4 + a++ = ' + (4 + a++) + '<br>');
        a = 5;
        document.write('4 + --a = ' + (4 + --a) + '<br>');
        a = 5;
        document.write('4 + a-- = ' + (4 + a--) + '<br>');
    </script>
    </body>
</html>

Avec ce résultat :

a = 5
++a = 6
-a = 6
a++ = 5
a- = 5
4 + ++a = 10
4 + a++ = 9
4 + -a = 8
4 + a- = 9

L'opérateur « ++ » ajoute 1 et l'opérateur « — » enlève 1 à la variable. On parle d'incrémentation et de décrémentation.

Lorsque l'incrémentation (ou la décrémentation) est avant la variable, on l'applique avant l'opérateur et s'il est situé après on l'applique ensuite. Ce n'est pas clair ? Alors analysez le code !

I-D. Les boucles et instructions conditionnelles

On a vu qu'un script JavaScript est une suite d'instructions. Il est souvent nécessaire de rompre la séquentialité pour effecteur des boucles, des sauts… voyons comment faire.

I-D-1. Les boucles

Regardez ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        for (var i = 0; i < 10; ++i) {
            document.write('i = ' + i + '<br>');
        }
    </script>
    </body>
</html>

Avec ce résultat :

i = 0
i = 1
i = 2
i = 3
i = 4
i = 5
i = 6
i = 7
i = 8
i = 9

L'instruction for permet d'itérer en fixant une limite, ici i < 10.

On peut aboutir au même résultat en utilisant l'instruction while :

 
Sélectionnez
1.
2.
3.
4.
5.
var i = 0;
while (i < 10) {
    document.write('i = ' + i + '<br>');
    ++i
}

Il existe aussi l'instruction do...while où le test est effectué à la fin :

 
Sélectionnez
1.
2.
3.
4.
var i = 0;
do {
    document.write('i = ' + i + '<br>');
} while(++i < 10)

Remarquez que cette fois j'ai utilisé l'opérateur d'incrémentation.

I-D-2. Les conditions

Regardez ce code :

 
Sélectionnez
1.
2.
3.
4.
var age = 20;
if(age > 18) {
    document.write('Vous êtes majeur');
}

L'instruction if permet de définir du code conditionnel. Si age > 18 est true alors le code inclus entre les accolades (on appelle ceci un bloc de code) est exécuté, sinon on continue avec la suite.

On peut prévoir une condition alternative :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
var age = 14;
if(age < 18) {
    document.write('Vous êtes mineur');
} else {
    document.write('Vous êtes majeur');
}

Dans ce cas on a affaire à un mineur. On peut aussi enchaîner les conditions :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
var age = 14;
if(age < 10) {
    document.write('Vous êtes de la première décade');
} else if(age < 20) {
    document.write('Vous êtes de la deuxième décade');
} else if(age < 30) {
    document.write('Vous êtes de la troisième décade');
} else {
    document.write('Vous êtes au-delà de la troisième décade');
}

Il existe aussi l'instruction switch :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
var count = 3;
switch(count) {
    case(1): document.write('Vous êtes au départ');
        break;
    case(2): document.write('Vous êtes bien parti');
        break;
    case(3): document.write('Vous êtes presque arrivé');
        break;
    default: document.write('Vous avez fini');
}

Avec ce résultat :

Vous êtes presque arrivé

I-E. Les fonctions

Une fonction est un bloc d'instructions qui est défini à un endroit, mais peut être exécuté ailleurs autant de fois que l'on veut. Regardez cet exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<!DOCTYPE html>
<html>
    <head>
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        function ma_fonction() {
            document.write("Coucou !" + '<br>');
        }
        ma_fonction();
        ma_fonction();
    </script>
    </body>
</html>

Avec ce résultat :

Coucou !
Coucou !

Une fonction se définit avec le mot clé function. On précise ensuite le nom de la fonction, on place des parenthèses dont nous allons voir bientôt l'intérêt et enfin on place le bloc de code entre des accolades. Pour l'appeler, on utilise le nom de la fonction suivi des parenthèses.

Voici un autre exemple :

 
Sélectionnez
1.
2.
3.
4.
function ma_fonction(a, b) {
    return 2 * (a + b);
}
document.write("Le résultat est : " + ma_fonction(3, 4));

Avec le résultat :

Le résultat est : 14

On appelle a et b les arguments de la fonction. Lorsqu'on appelle la fonction, il faut transmettre une valeur pour les arguments.

On peut aussi déclarer une variable pour contenir une fonction. Regardez ce code :

 
Sélectionnez
1.
2.
3.
4.
var action = function (a, b) {
    return 2 * (a + b);
}
document.write("Le résultat est : " + action(3, 4));

On obtient ainsi le même résultat, alors quel intérêt ? Dans ce cas aucun, mais le fait d'avoir une fonction dans une variable permet de la passer comme argument d'une autre fonction :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
var somme = function (a, b) {
    return a + b;
}
var produit = function (a, b) {
    return a * b;
}
function resultat(x, a, b) {
    document.write("Le résultat est : " + x(a, b) + '<br>');
}
resultat(somme, 3, 4);  
resultat(produit, 3, 4);

Avec cet affichage :

Le résultat est : 7
Le résultat est : 12

On passe une fonction comme premier argument de resultat.

I-F. Les fermetures (closures)

Voyons maintenant quelque chose de plus délicat avec les fermetures (closures). Dans un premier temps, regardez ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        function ajouteDouble(a, b) {
            function double(n) {
                return 2 * n;
            }
            return double(a) + double(b);
        }
        document.write("Le résultat est : " + ajouteDouble(3, 4) + '<br>');
    </script>
    </body>
</html>

Le rendu est :

Le résultat est : 14

On a une fonction dans une fonction. Quelle curieuse idée ! On aurait pu avoir une fonction double au même niveau que ajouteDouble et ça fonctionnerait de la même façon. La différence est que la fonction incluse ne peut pas être atteinte en dehors de la fonction englobante. Elle a accès aux variables de la fonction englobante, mais l'inverse n'est pas vrai.

Voici un autre exemple pour aller plus loin :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
function multiplier(a) {
    return function(b) {
        return a * b;
    }
}
var multipliePar5 = multiplier(5);
var multipliePar6 = multiplier(6);
document.write("Le résultat est : " + multipliePar5(4) + '<br>');
document.write("Le résultat est : " + multipliePar6(4) + '<br>');

Ce qui donne :

Le résultat est : 20
Le résultat est : 24

On crée des fonctions à partir de multiplier. Ces fonctions sont des fermetures (closures), elles conservent le contexte de leur création et elles deviennent accessibles en dehors de la fonction englobante. Par exemple multipliePar5 se rappelle qu'on lui a transmis la valeur 5 et on peut l'utiliser n'importe où.

Les fermetures sont beaucoup utilisées dans les bibliothèques JavaScript.

I-G. Les tableaux

Les tableaux servent à mémoriser plusieurs valeurs dans une seule variable, peu importe leur type. Voici comment créer et parcourir un tableau :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var tableau = [];
        tableau[0] = "Pierre";
        tableau[1] = "Paul";
        tableau[2] = "Arthur";
        for(var i = 0; i < tableau.length; ++i) {
            document.write("Le prénom à l'index " + i + ' est ' + tableau[i] + '<br>');
        }
    </script>
    </body>
</html>

Avec ce résultat :

Le prénom à l'index 0 est Pierre
Le prénom à l'index 1 est Paul
Le prénom à l'index 2 est Arthur

On peut initialiser directement les valeurs d'un tableau dans les accolades. Et on peut évidemment modifier les valeurs d'un tableau en utilisant l'index :

 
Sélectionnez
1.
2.
3.
4.
5.
var tableau = ["Pierre", "Paul", "Arthur"];
tableau[0] = "Simon";
for(var i = 0; i < tableau.length; ++i) {
    document.write("Le prénom à l'index " + i + ' est ' + tableau[i] + '<br>');
}

Ce qui donne :

Le prénom à l'index 0 est Simon
Le prénom à l'index 1 est Paul
Le prénom à l'index 2 est Arthur

La méthode length renvoie l'indice le plus grand + 1. On n'est donc pas vraiment sûrs d'obtenir ainsi le nombre total d'éléments. Voici une méthode plus sûre :

 
Sélectionnez
1.
2.
3.
4.
var tableau = ["Pierre", "Paul", "Arthur"];
tableau.forEach(function(valeur, index, tableau) {
    document.write("Le prénom à l'index " + index + ' est ' + valeur + '<br>');
});

Vous obtenez :

Le type est object

Nous allons voir juste après les objets et ce que je vais en dire concerne donc aussi les tableaux. La différence c'est que les tableaux ont une indexation numérique alors que les objets ont une indexation nominale.

I-H. Les objets

J'ai dit plus haut que JavaScript est orienté « objet ». Nous allons voir maintenant ce que ça signifie. C'est un point important de ce langage qui doit bien être compris.

I-H-1. Créer un objet

Voici un premier exemple où nous allons créer un objet :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var mon_objet = {};
        mon_objet.nom = "Dupont";
        mon_objet.prenom = "Pierre";
        document.write('Mon nom est ' + mon_objet.nom + '<br>');
        document.write('Mon prénom est ' + mon_objet.prenom + '<br>');
    </script>
    </body>
</html>

Avec ce résultat :

Mon nom est Dupont
Mon prénom est Pierre

Regardez la syntaxe utilisée. On crée l'objet avec des accolades vides. Ensuite on déclare des propriétés auxquelles on affecte des valeurs.

Cette façon de créer un objet est nommée syntaxe littérale. On peut aussi le faire de façon plus classique avec le mot clé new :

 
Sélectionnez
var mon_objet = new Object();

Voici une autre syntaxe très utilisée pour aboutir au même résultat :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
var mon_objet = {
    nom: "Dupont",
    prenom: "Pierre"
};
document.write('Mon nom est ' + mon_objet.nom + '<br>');
document.write('Mon prénom est ' + mon_objet.prenom + '<br>');

Cette fois on déclare et affecte les propriétés directement entre les accolades. Remarquez l'utilisation du double point pour l'affectation dans ce cas.

I-H-2. Les propriétés

On peut évidemment changer la valeur d'une propriété :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
var mon_objet = {
    nom: "Dupont",
    prenom: "Pierre"
};
mon_objet.nom = "Durand";
document.write('Mon nom est ' + mon_objet.nom + '<br>');
document.write('Mon prénom est ' + mon_objet.prenom + '<br>');

Avec ce résultat :

Mon nom est Durand
Mon prénom est Pierre

Voici une autre syntaxe pour un résultat identique :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
var mon_objet = {
    nom: "Dupont",
    prenom: "Pierre"
};
mon_objet["nom"] = "Durand";
document.write('Mon nom est ' + mon_objet.nom + '<br>');
document.write('Mon prénom est ' + mon_objet.prenom + '<br>');

On peut donc au choix utiliser la syntaxe avec le point ou sous la forme de tableau, dans ce cas on place le nom de la propriété entre guillemets (ou apostrophes). Cette seconde syntaxe permet une approche dynamique. Regardez ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
var mon_objet = {nom: "Dupont"};
var propriete = 'prenom';
mon_objet[propriete] = "Pierre";
document.write('Mon nom est ' + mon_objet.nom + '<br>');
document.write('Mon prénom est ' + mon_objet.prenom + '<br>');

Le nom de la propriété est devenu dynamique, ce qui ouvre pas mal de perspectives de codage !

I-H-3. Les méthodes

Les objets ont des propriétés, mais ils peuvent aussi avoir des comportements, il suffit de leur ajouter des méthodes. Regardez ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
<!DOCTYPE html>
<html>
    <head>
        <meta charset="utf-8">
        <title>JavaScript</title>
    </head>
    <body>
    <script>
        var mon_objet = {
            nom: "Durand",
            prenom: "Pierre",
            identite: function () {
                document.write('Mon nom est ' + this.nom + '<br>');
                document.write('Mon prénom est ' + this.prenom + '<br>');
            }
        };
        mon_objet.identite();
    </script>
    </body>
</html>

Avec ce résultat :

Mon nom est Durand
Mon prénom est Pierre

On a défini une méthode identite qui est chargée d'afficher le nom et le prénom. Remarquez l'utilisation du mot clé this qui sert à référencer l'objet en cours. L'appel de la méthode se fait simplement avec la syntaxe avec le point.

I-I. En résumé

  • JavaScript est un langage dynamique, orienté objet.
  • JavaScript dispose de types et d'opérateurs.
  • JavaScript permet de créer des variables, des fonctions et des objets.

II. On se lance !

Comme je l'ai dit dans le précédent chapitre, vue.js est une bibliothèque pour créer facilement des interfaces web interactives. Dans ce chapitre, nous allons voir dans un premier temps comment l'installer et dans un deuxième temps les concepts clés et un petit exemple de réalisation pour illustrer.

Il existe un site très bien fait auquel vous pouvez vous référer. Mais évidemment tout y est en anglais.

II-A. Installation

Pour installer vue.js, c'est un peu plus simple puisque c'est juste une bibliothèque JavaScript. Vous avez donc deux possibilités…

II-A-1. La charger et la mettre sur votre serveur

Il suffit de récupérer la bibliothèque en cliquant sur ce bouton sur le site :

Image non disponible

Au moment où j'écris ces lignes, la version est la 1.0.16, mais il est fort probable que vous trouviez une version plus récente.

Vous n'avez plus ensuite qu'à prévoir une balise script :

 
Sélectionnez
<script src="/js/vue.min.js"></script>

Adaptez évidemment le chemin selon votre contexte.

II-A-2. Utiliser un CDN

Dans ce cas c'est encore plus simple : vous avez le lien disponible sur le site au même emplacement que vu ci-dessus. Il suffit ensuite de prévoir une balise script :

 
Sélectionnez
<script src="http://cdn.jsdelivr.net/vue/1.0.16/vue.min.js"></script>

Là aussi il est fort probable que vous aurez une version plus récente.

II-B. Le modèle MVVM

Vue.js utilise le modèle MVVM, autrement dit Modèle-Vue-VueModèle. Voyons ça de plus près. C'est un modèle assez proche du modèle MVC (Modèle-Vue-Contrôleur) qui est plus connu, et du MVP (Modèle-Vue-Présentation) qui est moins connu. Le but est de bien séparer ce qui est présenté à l'écran (l'interface utilisateur) et la gestion.

Ce modèle a originellement été créé en 2005 par Microsoft pour le WPF et Silverlight. Mais il a ensuite été adopté dans certains frameworks JavaScript, comme KnockoutJS.

II-B-1. Le modèle

Le modèle représente les données nécessaires à l'application. Par exemple des données sur des livres : titre, année de parution, auteur, éditeur… Le modèle est chargé de gérer ces données : les mémoriser, les conserver, les restituer. Par contre il n'est pas censé effectuer de traitement sur ces données.

II-B-2. La vue

La vue représente ce qui est accessible à l'utilisateur : l'interface. La principale caractéristique de cette vue c'est qu'elle est interactive. Si l'utilisateur fait une action, la vue doit s'adapter en conséquence. Mais ce n'est pas elle qui comporte la logique nécessaire pour le faire, c'est la VueModèle.

II-B-3. La VueModèle

La VueModèle est l'intermédiaire entre la vue et le modèle. Elle transforme les données en éléments d'affichage dans la vue (data-binding) et elle gère les actions de l'utilisateur. La liaison entre la vue et la VueModèle est donc à double sens :

  • un changement dans le modèle doit être actualisé dans la vue ;
  • une action dans la vue doit être transmise au modèle.

Voici un petit schéma pour illustrer tout ça :

Image non disponible

II-C. Modèle MVVM dans vue.js

Voyons maintenant comment cela est implémenté dans vue.js.

II-C-1. VueModèle

On va voir ici d'emblée le principal objet de vue.js qui possède le constructeur Vue pour instancier une VueModèle. Donc à la base on va créer un objet :

 
Sélectionnez
var vm = new Vue({ /* options */ })

Cette vue sera associée à un élément du DOM. Je trouve un peu dommage la confusion générée avec l'appellation « Vue » étant donné que c'est une VueModèle. Mais on va voir à l'usage que ce n'est pas vraiment un souci.

Nous allons voir juste après à quoi correspondent les options transmises.

II-C-2. Vue

J'ai dit ci-dessus que la VueModèle est associée à un élément du DOM. La vue est justement constituée par cet élément du DOM. Il y a deux façons de déclarer cet élément, on peut le transmettre dans les options lorsqu'on crée la VueModèle :

 
Sélectionnez
var vm = new Vue({
  el: '#element'
});

Ou alors on le déclare ensuite :

 
Sélectionnez
var vm = new Vue(); 
vm.el = "#element";

II-C-3. Modèle

Le modèle est un objet JavaScript qu'on va associer à la VueModèle. Là encore on pourra le faire directement dans les options :

 
Sélectionnez
1.
2.
3.
4.
var vm = new Vue({
  el: '#element',
  data : {}
});

Ou ensuite :

 
Sélectionnez
1.
2.
3.
4.
var vm = new Vue({ 
  el: '#element'
}); 
wm.data = {};

Voilà les acteurs en place, mais pour le moment on ne peut pas en faire grand-chose, il va nous falloir des outils pour les mettre en relation pour créer de l'interactivité.

II-D. Les directives

Si vous utilisez AngularJS, vous êtes déjà habitué aux directives puisqu'ici c'est le même principe qui est appliqué, en plus simple.

Une directive est un attribut HTML qui dit de faire une action dans le DOM. Vue.js propose de nombreuses directives et on peut aussi en créer de nouvelles selon les besoins.

Prenons un exemple. Voici la partie HTML :

 
Sélectionnez
<div id="tuto">
  <p v-text="texte"></p>
</div>

Et voici la partie JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
var vm = new Vue({
  el: '#tuto',
  data: {
    texte: 'Mon texte'
  }
});

On crée une VueModèle vm, on lui associe l'élément du DOM qui a l'identifiant tuto (la vue), et les données data (le modèle).

Dans le HTML on a la directive v-text qui associe la propriété texte du modèle avec le contenu de la balise p du DOM. On va donc avoir comme contenu de la balise la valeur de la propriété texte. Et donc afficher :

Mon texte

Voici une petite illustration des liaisons :

Image non disponible

Là où ça devient intéressant, c'est que si cette valeur change l'affichage changera également. C'est facile à vérifier en ajoutant cette ligne à la fin du code vu ci-dessus :

 
Sélectionnez
setTimeout(function(){ vm.texte = 'Mon autre texte'; }, 3000);

Maintenant au bout de trois secondes l'affichage va changer pour :

Mon autre texte

Remarquez comment j'ai référencé la propriété :

 
Sélectionnez
vm.texte = 'Mon autre texte';

On obtient le même résultat que si on avait écrit :

 
Sélectionnez
vm.$data.texte = 'Mon autre texte';

Mais c'est bien plus lisible et pratique ainsi !

II-E. Mustache et filtres

II-E-1. Mustache

Rien à voir avec nos moustaches même si justement ce nom vient de la ressemblance entre les accolades et les moustaches (merci tofdesbois). Toujours est-il que la syntaxe mustache est devenue une référence pour le templating dans de multiples langages. La base en est une grande simplicité. Reprenons l'exemple vu ci-dessus, mais cette fois en nous passant de la directive :

 
Sélectionnez
<div id="tuto">
  <p>{{ texte }}</p>
</div>

On conserve le même code JavaScript et on obtient le même résultat. Le texte ainsi transmis est « échappé », c'est-à-dire que si vous prévoyez par exemple une balise HTML :

 
Sélectionnez
data: {
  texte: '<span>Mon texte</span>'
}

Le résultat sera rendu en texte :

<span>Mon texte</span>

Ceci pour des raisons de sécurité. Si vous tenez absolument à ne pas protéger les données, vous pouvez utiliser la syntaxe avec la triple accolade :

 
Sélectionnez
<p>{{{ texte }}}</p>

Mais dans ce cas, soyez prudent !

Voici une petite illustration des liaisons :

Image non disponible

II-E-2. Les filtres

Un filtre permet d'apporter des modifications aux données avant de les afficher. La meilleure façon de comprendre de quoi il s'agit est de prendre un exemple :

 
Sélectionnez
<p>{{ texte | uppercase }}</p>

On affiche de nouveau notre texte, mais cette fois on le veut en capitales. On obtient :

MON TEXTE

Il suffit d'ajouter le signe « | » suivi du nom du filtre. Nous en verrons d'autres dans les prochains articles. Il est aussi possible d'en créer, ce que nous ferons dans la deuxième partie.

II-F. Un exemple

Nous avons à présent suffisamment d'éléments pour construire un premier exemple. Nous allons faire un chronomètre qui se déclenche au chargement de la page et qui affiche les heures, les minutes et les secondes. Donc rien de bien extraordinaire, mais on va voir avec quelle facilité on peut le réaliser avec vue.js. Voilà le code complet de la page :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
<!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">
 
      <div id="tuto" class="text-center">
        <h1>Vous êtes là depuis :</h1>
        <h1>
          <span class="label label-primary">{{ heures }}</span> heures
          <span class="label label-primary">{{ minutes }}</span> minutes
          <span class="label label-primary">{{ secondes }}</span> secondes
        </h1>
      </div>
 
    </div>
 
    <script src="http://cdn.jsdelivr.net/vue/0.12.8/vue.min.js"></script>
 
    <script>
 
      var vm = new Vue({
        el: '#tuto',
        data: {
          heures: 0,
          minutes: 0,
          secondes: 0
        }
      });
 
      var totalSecondes = 0;
       
      setInterval(function() {
        var minutes = Math.floor(++totalSecondes / 60);
        vm.secondes = totalSecondes - minutes * 60;
        vm.heures = Math.floor(minutes / 60);
        vm.minutes = minutes - vm.heures * 60;
      }, 1000);
 
    </script>
 
  </body>
 
</html>

Avec cet aspect :

Image non disponible

Voyons un peu comment ça fonctionne…

La partie vue est ici :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<div id="tuto" class="text-center">
  <h1>Vous êtes là depuis :</h1>
  <h1>
    <span class="label label-primary">{{ heures }}</span> heures
    <span class="label label-primary">{{ minutes }}</span> minutes
    <span class="label label-primary">{{ secondes }}</span> secondes
  </h1>
</div>

On a la DIV englobante avec l'identifiant tuto. On a aussi trois emplacements « mustache » pour :

  • heures ;
  • minutes ;
  • secondes.

Le reste est juste de la mise en forme boostée par Bootstrap.

La partie JavaScript est constituée dans un premier temps par la déclaration de la VueModèle:

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
var vm = new Vue({
  el: '#tuto',
  data: {
    heures: 0,
    minutes: 0,
    secondes: 0
  }
});

On trouve l'élément du DOM désigné par la propriété el. On trouve aussi le modèle avec la propriété data. On trouve au niveau des données les trois entités dont nous avons besoin pour gérer le chronomètre. On sait aussi que si on modifie une valeur dans le modèle ça sera répercuté dans la vue. Il suffit donc de mettre à jour régulièrement ces valeurs pour faire fonctionner le chronomètre.

Cette partie est réalisée avec ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
var totalSecondes = 0;
 
setInterval(function() {
  var minutes = Math.floor(++totalSecondes / 60);
  vm.secondes = totalSecondes - minutes * 60;
  vm.heures = Math.floor(minutes / 60);
  vm.minutes = minutes - vm.heures * 60;
}, 1000);

On initialise une variable totalSecondes qui va cumuler les secondes qui défilent. Ensuite on met en marche un timer avec un pas de 1 seconde et on actualise à chaque fois les données du modèle.

On n'a pas à se soucier de la manipulation du DOM, ce qui nous incomberait avec juste du JavaScript ou avec JQuery. Ici la liaison est automatiquement créée entre le modèle et la vue par la VueModèle.

II-G. En résumé

  • Vue.js implémente le modèle MVVM.
  • Vue.js offre des directives pour agir sur le DOM.
  • Vue.js offre la syntaxe mustache et des filtres pour la mise en forme de l'affichage.

III. Action !

Je vous ai dit dans le premier 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.

III-A. 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 :

 
Sélectionnez
<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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
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 :

Image non disponible

Voici une illustration de la liaison :

Image non disponible

On commence à avoir de l'interactivité !

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

 
Sélectionnez
1.
2.
3.
4.
5.
<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.

Voici le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
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 :

Image non disponible

Un bouton pour incrémenter une valeur

Voici une schématisation du fonctionnement :

Image non disponible

III-B. Un survol

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 :

 
Sélectionnez
1.
2.
3.
4.
5.
<div id="tuto">
  <h1> 
    <span class="label label-{{type}}" v-on:mouseover="changeType">{{type}}</span>
  </h1>
</div>

On voit une petite nouveauté avec du mustache dans les attributs. Autrement dit on va pouvoir changer de façon dynamique la valeur d'un attribut. 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.

Pour fonctionner, cet exemple nécessite Bootstrap. Donc prévoyez le chargement :

 
Sélectionnez
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.6/css/bootstrap.min.css">

Voici le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
new Vue({
  el: '#tuto',
  data: {
    type: 'primary'
  },
  methods: {
    changeType: function() {
      this.type = (this.type == 'primary') ? 'success' : 'primary';
    }
  }
});

On a dans les données la propriété type qui au départ a la valeur « primary ». À chaque survol de l'étiquette par le curseur de la souris cette valeur va changer, alternant entre « primary » et « 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 :

Image non disponible

Et ensuite :

Image non disponible

Et ainsi de suite à chaque survol…

Voici une illustration du fonctionnement :

Image non disponible

III-C. 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
<!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">
 
      <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" v-on:click="backward">Effacer</button>
          <button class="btn btn-danger btn-lg" v-show="etat.stop" v-on:click="stop">Arrêter</button>
          <button class="btn btn-primary btn-lg" v-show="etat.play" v-on:click="play">{{ btnPlay }}</button>
        </p>
      </div>
 
    </div>
 
    <script src="http://cdn.jsdelivr.net/vue/1.0.10/vue.min.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>

III-C-1. La vue

Voici la partie HTML :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<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" v-on:click="backward">Effacer</button>
    <button class="btn btn-danger btn-lg" v-show="etat.stop" v-on:click="stop">Arrêter</button>
    <button class="btn btn-primary btn-lg" v-show="etat.play" v-on: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 deux étiquettes avec une liaison « mustache ». On trouve également trois boutons de commande avec pour chacun :

  1. Une directive v-on pour détecter l'événement click ;
  2. Une 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.

La directive v-show fonctionne avec une valeur booléenne. Si celle-ci est vraie, l'élément est affiché, sinon il est masqué. On va s'en servir ici pour faire apparaître ou disparaître les boutons selon l'étape de fonctionnement du chronomètre.

III-C-2. Le JavaScript

Voyons la partie JavaScript. D'abord la déclaration propre à vue.js :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
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 trois propriétés :

    1. time : deux propriétés pour les minutes et secondes du chronomètre,
    2. btnPlay : pour le texte du bouton (Démarrer/Continuer),
    3. etat : trois propriétés pour la visibilité des trois 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
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;        
};

III-C-2-a. Le fonctionnement

Au départ on a cet aspect :

Image non disponible

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 :

Image non disponible

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

Image non disponible

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.

III-D. En résumé

  • On gère les événements avec la directive v-on..
  • On masque les éléments du DOM avec la directive v-show.

IV. On se répète…

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 multiples 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.

IV-A. 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
<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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
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 :

Image non disponible

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 :

Image non disponible

IV-B. 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 :

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

Avec ce JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
new Vue({
  el: '#tuto',
  data: {
    fruits: [
      'pomme',
      'cerise',
      'abricot'
    ]
  }
});

Et ce résultat :

Image non disponible

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

 
Sélectionnez
<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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
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 :

Image non disponible

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

Voici une illustration de ce fonctionnement :

Image non disponible

IV-C. Itérer un objet

On a vu qu'on peut créer une répétition dans le DOM avec un tableau de valeurs. 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
<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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
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 :

Image non disponible

Voici une schématisation du fonctionnement :

Image non disponible

IV-D. 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 ?

IV-D-1. 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
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 deux 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.

IV-D-2. 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
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 deux secondes. Vous remarquerez que cette fois il a fallu affecter à nouveau le tableau pour prendre en compte les modifications.

IV-D-3. Les méthodes augmentées

Regardez maintenant cet exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
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 !

De la même manière existe la méthode $remove pour supprimer un élément de tableau.

IV-E. 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 :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
<!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 :

Image non disponible

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 :

Image non disponible

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 :

Image non disponible

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 :

Image non disponible

On peut rétablir une personne ou tout le monde 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 chapitres précédents.

Il existe un filtre (orderBy) pour ordonner une liste, mais lorsqu'on l'utilise on perd le lien entre l'index de la liste et celui du tableau et on ne peut plus gérer les événements. 

IV-F. 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.

V. Double liaison

Contrairement à ce que le titre de ce chapitre pourrait suggérer, il ne s'agit pas de mœurs dissolues, mais toujours de notre bibliothèque JavaScript. On a vu au cours des chapitres précédents qu'on peut facilement établir un lien entre le modèle et la vue et que, si le modèle change, alors la vue change aussi pour refléter ce changement.

Maintenant nous allons nous intéresser à l'inverse, autrement dit si on change quelque chose dans la vue est-ce que ça va modifier le modèle ? Comme vue.js est censé justement établir une double liaison, ça devrait pouvoir se faire.

V-A. La directive v-model

La directive v-model crée une double liaison entre le modèle et un contrôle de formulaire : input, select, checkbox, textarea… Donc tout changement dans le modèle est répercuté dans le contrôle et réciproquement. Prenons un petit exemple simple, voici le HTML :

 
Sélectionnez
<form id="tuto">
  <input type="text" v-model="texte"><p>{{ texte }}</p>
</form>

Et le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
new Vue({
  el: '#tuto',
  data: { texte: 'coucou' }
});

Le résultat est un contrôle de texte et un texte associé au-dessous. Lorsqu'on change le texte dans le contrôle, le texte associé change également :

Image non disponible

Voici une illustration du fonctionnement :

Image non disponible

Pour être sûr que ça marche dans les deux sens, vous pouvez ajouter cette ligne à la fin du JavaScript :

 
Sélectionnez
setTimeout(function(){ vm.texte = 'Nouveau texte'; }, 2000);

Et vous verrez que deux secondes après le chargement de la page le nouveau texte figurera aux deux emplacements. C'est bien de la double liaison !

V-B. Boutons radio et cases à cocher

Les boutons radio et les cases à cocher ont une particularité : ils fonctionnent en « tout ou rien ».

V-B-1. Boutons radio

Les boutons radio s'utilisent groupés puisqu'un seul peut être coché.

Voici un exemple de mise en œuvre HTML :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
<form id="tuto">
  <div class="radio">
    <label>
      <input type="radio" name="sexe" value="male" v-model="sexe"> Male
    </label>
  </div>
  <div class="radio">
    <label>
      <input type="radio" name="sexe" value="femelle" v-model="sexe"> Femelle
    </label>
  </div>
  <div class="form-control">
    On a choisi {{ sexe }}
  </div>
</form>

Et le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
new Vue({
  el: '#tuto',
  data: { sexe: 'male' }
});

On a donc besoin d'une seule propriété qui a pour valeur celle du bouton radio coché. Au départ on a ceci :

Image non disponible

Et quand on coche l'autre bouton on a :

Image non disponible

Voici une illustration du fonctionnement :

Image non disponible

V-B-2. Cases à cocher

Les cases à cocher peuvent être solitaires, contrairement aux boutons radio. Prenons un exemple…

Voici le HTML :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
<form id="tuto">
  <div class="checkbox">
    <label>
      <input type="checkbox" name="nom" value="valeur" v-model="checked"> Je suis <strong>{{ checked }}</strong>
    </label>
  </div>
</form>

Et le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
new Vue({
  el: '#tuto',
  data: { checked: false }
});

Au départ on a ceci :

Image non disponible

Au aurait évidemment la case cochée si on avait affecté true à checked.

Lorsqu'on clique, on obtient l'état inverse :

Image non disponible

V-C. Liste de choix

Une liste de choix propose une liste d'éléments, le plus souvent sous forme déroulante. C'est un contrôle très utilisé.

V-C-1. Sélection simple

Commençons avec un exemple simple, voici le HTML :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<form id="tuto">
  <select class="form-control" v-model="choix">
    <option>Choix 1</option>
    <option>Choix 2</option>
    <option>Choix 3</option>
  </select>
  <p>On a choisi "{{ choix }}"</p>
</form>

Et le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
new Vue({
  el: '#tuto',
  data: { choix: 'Choix 2' }
});

Au départ on a cette situation :

Image non disponible

Et selon ce qu'on sélectionne, le texte suit fidèlement :

Image non disponible

V-C-2. Sélections multiples

On peut désirer pouvoir choisir plusieurs éléments dans une liste de choix en utilisant l'attribut multiple. Voici notre exemple modifié en conséquence :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<form id="tuto">
  <select class="form-control" v-model="choix" multiple>
    <option>Choix 1</option>
    <option>Choix 2</option>
    <option>Choix 3</option>
  </select>
  <p>On a choisi "{{ choix }}"</p>
</form>

Et le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
new Vue({
  el: '#tuto',
  data: { choix: ['Choix 2', 'Choix 3'] }
});

Comme on a plusieurs choix possibles, on utilise un tableau de valeurs. Au départ on a :

Image non disponible

Et selon les choix suivants, l'affichage s'adapte :

Image non disponible

V-C-3. Options dynamiques

On a souvent besoin de remplir de façon dynamique une liste de choix. Pour le faire, on met à contribution la directive v-for qu'on connaît déjà. Mais il faut également gérer la liaison entre les options et leurs valeurs dans le modèle, on le fait avec une directive v-bind :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<form id="tuto">
  <select class="form-control" v-model="choix" multiple>
    <option v-for="option in options" v-bind:value="option.value">
      {{ option.text }}
    </option>
  </select>
  <p>On a choisi "{{ choix }}"</p>
</form>

Et le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
new Vue({
  el: '#tuto',
  data: { 
    choix: [1, 3],
    options: [
      { text: 'Choix 1', value: '1' },
      { text: 'Choix 2', value: '2' },
      { text: 'Choix 3', value: '3' }
    ],
  }
});

V-D. Un exemple

Nous allons poursuivre l'exemple du précédent chapitre en ajoutant la possibilité de modifier les données d'une personne et aussi d'en ajouter une. Voici le code complet :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
108.
109.
110.
111.
112.
113.
114.
115.
116.
117.
118.
119.
120.
121.
122.
123.
124.
125.
126.
127.
128.
129.
130.
131.
132.
133.
134.
135.
136.
137.
138.
139.
140.
141.
142.
143.
144.
145.
146.
<!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">
        <div class="panel-heading">Personnes actives</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 personnes">
              <td>{{ personne.nom }}</td>
              <td>{{ personne.prenom }}</td> 
              <td><button class="btn btn-info btn-block" v-on:click="modifier($index)">Modifier</button></td>
              <td><button class="btn btn-warning btn-block" v-on:click="supprimer($index)">Poubelle</button></td>
            </tr>  
            <tr>
              <td><input type="text" class="form-control" v-model="inputNom" v-el:modif placeholder="Nom"></td>
              <td><input type="text" class="form-control" v-model="inputPrenom" placeholder="Prénom"></td>
              <td colspan="2"><button class="btn btn-primary btn-block" v-on:click="ajouter()">Ajouter</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>
 
      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: [],
          inputNom: '',
          inputPrenom: ''
        },
        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 = [];
          },
          ajouter: function() {
            this.personnes.push({nom: this.inputNom, prenom: this.inputPrenom});
            this.inputNom = this.inputPrenom = '';
            this.personnes.sort(ordonner);
          },
          modifier: function(index) {
            this.inputNom = this.personnes[index].nom;
            this.inputPrenom = this.personnes[index].prenom;
            this.personnes.splice(index, 1);
            this.$$.modif.focus();
          }
        }
      });
 
      var ordonner = function (a, b) { 
        return (a.nom.toUpperCase() > b.nom.toUpperCase())
      };
 
    </script>
 
  </body>
 
</html>

Nous allons nous intéresser à ce qui a été ajouté.

Lorsqu'on lance la page, on obtient ceci :

Image non disponible

V-D-1. Ajouter une personne

On voit la présence d'un formulaire sur la dernière ligne du tableau. Si on insère un nom, il va naturellement prendre sa place dans la liste et le formulaire se remet en situation de départ. Voyons comment cela est réalisé.

Voici le code du formulaire :

 
Sélectionnez
<td><input type="text" class="form-control" v-model="inputNom" v-el:modif placeholder="Nom"></td>
<td><input type="text" class="form-control" v-model="inputPrenom" placeholder="Prénom"></td>
<td colspan="2"><button class="btn btn-primary btn-block" v-on:click="ajouter()">Ajouter</button></td>

Les deux zones de texte sont équipées d'une directive v-model que vous connaissez bien désormais. Le bouton a une directive v-on pour la gestion de l'événement click.

Par contre il y a une nouvelle directive : v-el. Celle-là nous n'avons pas encore eu l'occasion de la rencontrer. Mais c'est sans doute la plus simple de vue.js. Le but est de pouvoir identifier l'élément du DOM concerné. Cela va nous servir plus loin pour placer le focus.

Pour gérer ces deux contrôles, ont été ajoutées deux propriétés dans le modèle :

 
Sélectionnez
1.
2.
3.
4.
5.
data: {
  ...
  inputNom: '',
  inputPrenom: ''
},

Pour l'ajout de la personne, on a prévu la méthode ajouter :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
methods: {
   
    ...
     
  ajouter: function() {
    this.personnes.push({nom: this.inputNom, prenom: this.inputPrenom});
    this.inputNom = this.inputPrenom = '';
    this.personnes.sort(ordonner);
  },
 
    ...
}
  • on ajoute les données saisies aux personnes ;
  • on réinitialise les contrôles ;
  • enfin on force le classement pour bien placer alphabétiquement la nouvelle personne.

Je ne fais aucun contrôle de saisie pour conserver la simplicité de l'exemple.

V-D-2. Modifier une personne

On dispose d'un bouton de modification pour chaque personne de la liste :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
<tr v-for="personne in personnes">
  <td>{{ personne.nom }}</td>
  <td>{{ personne.prenom }}</td> 
  <td><button class="btn btn-info btn-block" v-on:click="modifier($index)">Modifier</button></td>
  <td><button class="btn btn-warning btn-block" v-on:click="supprimer($index)">Poubelle</button></td>
</tr>

Et on a la méthode modifier :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
methods: {
   
  ...
 
  modifier: function(index) {
    this.inputNom = this.personnes[index].nom;
    this.inputPrenom = this.personnes[index].prenom;
    this.personnes.splice(index, 1);
    this.$$.modif.focus();
  }
}
  • on place dans les deux zones de texte le nom et le prénom ;
  • on supprime la personne de la liste ;
  • on met le focus dans la zone de texte du prénom (on voit là l'intérêt d'avoir mis une directive v-el dans ce contrôle. Remarquez la syntaxe un peu particulière).

Ce qui donne ce genre d'affichage :

Image non disponible

Dupont est en saisie et il a disparu de la liste. On peut modifier nom et prénom. Quand on clique sur « Ajouter » les données modifiées vont prendre leur place dans la liste comme on l'a vu ci-dessus dans le cas de l'ajout d'une personne.

V-E. En résumé

  • La directive v-model permet d'établir une double liaison.
  • Tous les contrôles de formulaire peuvent utiliser cette directive.
  • Pour remplir de façon dynamique une liste de choix on utilise les directives v-for et v-bind.
  • La directive v-el permet d'identifier un élément du DOM.

VI. filtrage

Nous avons bien avancé dans l'apprentissage de vue.js. Nous savons comment établir des liaisons, parfois à double sens, entre la vue et le modèle. Jusque là nous n'avons effectué aucun traitement intermédiaire et les données du modèle se retrouvent telles qu'elles dans la vue, et réciproquement. Parfois nous avons besoin d'effectuer quelques modifications, soit avant affichage, soit avant mémorisation des données. Ce tutoriel est destiné à présenter cet aspect de vue.js.

VI-A. Les filtres

Dans vue.js un filtre est une fonction qui transforme une donnée :

Image non disponible

On a une donnée à l'entrée, elle est transformée et on la retrouve à la sortie dans un nouvel état.

VI-A-1. Un simple filtre

Voici un exemple simple :

 
Sélectionnez
<div id="tuto">
  La donnée est "{{ texte }}", mais elle est transformée en "{{ texte | capitalize }}".
</div>

Et le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
new Vue({
  el: '#tuto',
  data: { texte: 'coucou' }
});

Avec comme résultat :

La donnée est « coucou », mais elle est transformée en « Coucou ».

Pour ajouter un filtre, on utilise le signe « | » suivi du nom du filtre. Ici capitalize met en majuscule la première lettre.

Voici une illustration du fonctionnement :

Image non disponible

VI-A-2. Double filtrage

On peut cumuler les filtres :

 
Sélectionnez
<div id="tuto">
  La donnée est "{{ texte }}", mais elle est transformée en "{{ texte | lowercase | capitalize }}".
</div>

Avec ce JavaScript :

 
Sélectionnez
1.
2.
3.
4.
new Vue({
  el: '#tuto',
  data: { texte: 'COUCOU' }
});

On obtient :

La donnée est « COUCOU », mais elle est transformée en « Coucou ».

Pour mettre en œuvre plusieurs filtres, il suffit de les ajouter, toujours avec le signe « | ». Ici on commence par tout mettre en minuscules avec le filtre lowercase, puis on met en majuscule le premier caractère avec capitalize.

Voici une illustration du fonctionnement :

Image non disponible

VI-B. Le pluriel

On a souvent l'occasion de jouer avec des pluriels lorsqu'on affiche des données. En général on se retrouve à effectuer un test de quantité pour savoir s'il faut ajouter ou non un « s » à la fin d'un mot. Vue.js propose un filtre spécifique à ce problème :

 
Sélectionnez
1.
2.
3.
4.
5.
<ul id="tuto">
  <li v-for="animal in animaux">
    {{ animal.nom }} : {{ animal.nombre }} {{ animal.nombre | pluralize 'présent' }}
  </li>
</ul>

Avec ce JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
new Vue({
  el: '#tuto',
  data: { 
    animaux: [
      { nom: "Chat", nombre: 1 },
      { nom: "Chien", nombre: 3 },
      { nom: "Souris", nombre: 1 }
    ]
  }
});
Image non disponible

Le filtre pluralize met le mot qu'on lui passe comme argument, ici « présent », au pluriel en lui ajoutant un « s » selon la valeur filtrée, ici « nombre ». Donc quand « nombre » est plus grand que 1 il ajoute un « s » à « présent ».

Vous allez dire que bien souvent dans la langue française on n'a pas un « s » pour mettre au pluriel. Dans ce cas on peut utiliser plusieurs arguments. Voici un exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
<ul id="tuto">
  <li v-for="animal in animaux">
    {{ animal.nom }} : {{ animal.nombre }} {{ animal.nombre | pluralize 'animal' 'animaux' }}
  </li>
</ul>

Avec ce JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
new Vue({
  el: '#tuto',
  data: { 
    animaux: [
      { nom: "Chat", nombre: 1 },
      { nom: "Chien", nombre: 3 },
      { nom: "Souris", nombre: 2 }
    ]
  }
});

Et ce résultat :

Image non disponible

VI-C. Le clavier

On a vu des cas de gestion d'un événement de la souris. On peut de la même manière gérer un événement du clavier. Mais dans ce cas on a pratiquement toujours besoin de savoir quelle touche du clavier a été utilisée. C'est ici qu'intervient le filtre key.

Considérez ce cas :

 
Sélectionnez
<div id="tuto">
  <input v-on:keypress="action">
</div>

On a une zone de texte et on écoute l'événement keypress. Voici le JavaScript pour traiter cet événement :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
new Vue({
  el: '#tuto',
  methods: { 
    action: function() {
      alert('On a actionné une touche !')
    }
  }
});

Lorsque le focus est placé dans la zone de texte, quelle que soit la touche actionnée on obtient l'affichage de la petite fenêtre d'information :

Image non disponible

Pour prendre compte seulement une certaine touche on doit utiliser le filtre key :

 
Sélectionnez
<input v-on:keypress.left="action">

On change le texte de l'alerte :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
new Vue({
  el: '#tuto',
  methods: { 
    action: function() {
      alert('On a actionné la touche "flèche à gauche" !')
    }
  }
});

Maintenant on aura la fenêtre que si on actionne la touche « flèche à gauche » :

Image non disponible

Voici une schématisation du fonctionnement :

Image non disponible

On obtiendrait le même résultat en transmettant le code de la touche :

 
Sélectionnez
<input v-on:keypress.37="action">

Vue.js possède des alias pour les touches les plus utilisées :

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

Vous avez donc le choix d'utiliser ces alias ou directement le code.

VI-D. Filtrer une liste

On a aussi souvent besoin de filtrer des éléments d'une liste. Vue.js possède le filtre filterBy pour le faire. Voici un exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
<ul id="tuto">
  <li v-for="personne in personnes | filterBy 'actif'">
    {{ personne.nom }}
  </li>
</ul>

Et le JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
new Vue({
  el: '#tuto',
  data: {
    personnes: [
      { nom: "Claret", statut: "actif" },
      { nom: "Dupont", statut: "actif" },
      { nom: "Durand", statut: "passif" },            
      { nom: "Martin", statut: "actif" },
      { nom: "Torlet", statut: "passif" }            
    ]
  }
});

Avec ce rendu :

Image non disponible

Les personnes ne sont prises en compte que si on trouve « actif » parmi les valeurs.

Mais que se passe-t-il si l'une des personnes s'appelle « Actif » :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
personnes: [
  { nom: "Claret", statut: "actif" },
  { nom: "Dupont", statut: "actif" },
  { nom: "Actif", statut: "passif" },            
  { nom: "Martin", statut: "actif" },
  { nom: "Torlet", statut: "passif" }            
]

Dans ce cas on va le retrouver dans le rendu alors que son statut est « passif » :

Image non disponible

Dans ce cas on peut préciser sur quelle propriété on veut effectuer le filtrage :

 
Sélectionnez
<li v-for="personne in personnes | filterBy 'actif' in 'statut'">

Cette fois monsieur Actif ne sera pas sélectionné !

Voici une schématisation :

Image non disponible

Évidemment la valeur de filtrage peut être dans une variable et on peut ainsi envisager des choses intéressantes comme nous le verrons dans l'exemple de fin.

VI-E. Un peu d'ordre

Lorsqu'on crée à l'affichage une liste avec la directive v-for les données sont prises en compte dans l'ordre du modèle. Si on veut modifier cet ordre, par exemple avoir un ordre alphabétique, on peut évidemment réordonner ces données dans le modèle, mais on peut aussi utiliser le filtre orderBy.

Voici un exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
<ul id="tuto">
  <li v-for="personne in personnes | orderBy 'nom'">
    {{ personne.nom }}
  </li>
</ul>

Ici on a ajouté le filtre orderBy en précisant qu'il doit agir sur la propriété « nom ».

Avec ce JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
new Vue({
  el: '#tuto',
  data: {
    personnes: [
      { nom: "Martin"},
      { nom: "Claret"},
      { nom: "Durand"},
      { nom: "Dupont"},
      { nom: "Torlet"} 
    ]
  }
});

On voit que les noms ne sont pas dans l'ordre alphabétique au niveau du modèle. Pourtant on obtient grâce au filtre ce résultat :

Image non disponible

Voici une schématisation :

Image non disponible

On peut obtenir l'ordre inverse en ajoutant un deuxième argument qui doit être négatif :

 
Sélectionnez
<li v-for="personne in personnes | orderBy 'nom' -1">

Si on met -1 comme ici alors l'ordre est inversé :

Image non disponible

Si on prévoit une valeur positive on aura l'ordre normal, donc autant dans ce cas ne rien mettre !

On peut aussi prévoir de gérer dynamiquement l'ordre de sortie avec une variable.

VI-F. Un exemple

Voyons à présent un exemple récapitulatif pour voir les filtres en action dans une situation réaliste. On va reprendre le cas d'un tableau de personnes, mais cette fois on va gérer l'ordre alphabétique normal ou inverse par colonne et on va ajouter la possibilité de filtrer les lignes. Voici le code complet de l'exemple :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
101.
102.
103.
104.
105.
106.
107.
<!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">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
  </head>
 
  <body>
 
    <div class="container" id="tuto">
      <br>
 
      <div class="panel panel-primary">
        <div class="panel-heading">Personnes</div>        
        <table class="table table-bordered table-striped">
          <thead>
            <tr>
              <th v-for="head in heads">
                {{ head.titre }}
                <a href="#" v-on:click="action($index)">
                  <span class="fa fa-fw fa-sort{{ head.classe }}"></span>
                </a>
              </th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="
                personne in personnes
                | filterBy search in filterKey
                | orderBy sortKey orderKey">
              <td>{{ personne.nom | uppercase }}</td>
              <td>{{ personne.prenom }}</td> 
              <td>{{ personne.statut }}</td>
            </tr>  
          </tbody>       
        </table>
      </div> 
 
      <form>
        <div class="form-group">
          <input type="text" class="form-control" placeholder="Recherche ici" v-model="search">
        </div>
        <div class="radio" v-for="head in heads">
          <label>
            <input type="radio" value="{{ head.sort }}" v-model="filterKey">{{ head.titre }}
          </label>
        </div>
      </form>
 
    </div>
 
    <script src="http://cdn.jsdelivr.net/vue/1.0.10/vue.min.js"></script>
 
    <script>
 
      var classOrder = ['', '-desc', '-asc'];
      var keyOrder = ['', 1, -1];
 
      new Vue({
        el: '#tuto',
        data: {
          personnes: [
            { nom: "Claret", prenom: "Marcel", statut: 'Administrateur' },
            { nom: "Durand", prenom: "Jacques", statut: 'Utilisateur' }, 
            { nom: "Dupont", prenom: "Albert", statut: 'Utilisateur' },
            { nom: "Torlet", prenom: "Arthur", statut: 'Rédacteur' },            
            { nom: "Martin", prenom: "Denis", statut: 'Utilisateur' }                       
          ],
          heads: [
            { titre: 'Nom', classe: '', order: 0, sort: 'nom' },
            { titre: 'Prénom', classe: '', order: 0, sort: 'prenom' },
            { titre: 'Statut', classe: '', order: 0, sort: 'statut' }
          ],
          sortKey: '',
          orderKey: '',
          search: '',
          filterKey: 'nom'
        },
        methods: {
          action: function(index) {
            var self = this;
            this.heads.forEach(function(el) {
              if(el.sort != self.heads[index].sort) {
                el.order = 0;
                el.classe = '';
              };
            });
            var info = this.heads[index];     
            if(++info.order == 3) info.order = 1;
            info.classe = classOrder[info.order];
            this.heads.$set(index, info)
            this.orderKey = keyOrder[info.order];
            this.sortKey = info.sort;
          }
        }
      });
 
    </script>
 
  </body>
 
</html>

VI-F-1. Fonctionnement

Au départ, on a cet aspect :

Image non disponible

On a trois colonnes pour le nom, le prénom et le statut. Dans la partie inférieure, une zone de texte permet de filtrer la liste et on peut choisir entre les colonnes pour ce filtrage. Il est prévu des petites icônes pour figurer le tri. Au départ aucun tri n'étant effectué on a les deux petits triangles. Si on clique dessus on ordonne selon la colonne, par exemple pour les prénoms :

Image non disponible

La petite icône change pour figurer le tri décroissant. Si on clique à nouveau on passe en mode croissant et la petite icône change encore :

Image non disponible

Si maintenant on clique sur l'icône de la colonne des noms, on remet au repos celle des prénoms et le tri se fait alors sur la colonne des noms :

Image non disponible

Et ainsi de suite pour chaque colonne.

Pour le filtrage c'est plus simple : on choisit la colonne avec les boutons radio et on tape les caractères désirés :

Image non disponible

VI-F-2. Remplissage du tableau

Toutes les données pour remplir le tableau se trouvent dans le modèle :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
data: {
  personnes: [
    { nom: "Claret", prenom: "Marcel", statut: 'Administrateur' },
    { nom: "Durand", prenom: "Jacques", statut: 'Utilisateur' }, 
    { nom: "Dupont", prenom: "Albert", statut: 'Utilisateur' },
    { nom: "Torlet", prenom: "Arthur", statut: 'Rédacteur' },            
    { nom: "Martin", prenom: "Denis", statut: 'Utilisateur' }                       
  ],
  heads: [
    { titre: 'Nom', classe: '', order: 0, sort: 'nom' },
    { titre: 'Prénom', classe: '', order: 0, sort: 'prenom' },
    { titre: 'Statut', classe: '', order: 0, sort: 'statut' }
  ],

Un tableau d'objets pour les lignes et un autre pour les titres des colonnes et d'autres informations que nous allons voir plus loin.

L'en-tête du tableau est rempli avec ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
<th v-for="head in heads">
  {{ head.titre }}
  <a href="#" v-on:click="action($index)">
    <span class="fa fa-fw fa-sort{{ head.classe }}"></span>
  </a>
</th>

On utilise la directive v-for sur la propriété heads. On utilise mustache pour insérer le titre. On prévoit ensuite un lien pour l'icône de tri avec une directive v-on pour gérer l'événement en appelant la méthode action avec l'index comme paramètre. On gère le changement de l'aspect de l'icône en jouant sur la classe avec la propriété classe.

Le corps du tableau est rempli avec ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<tr v-for="
    personne in personnes
    | filterBy search in filterKey
    | orderBy sortKey orderKey">
  <td>{{ personne.nom | uppercase }}</td>
  <td>{{ personne.prenom }}</td> 
  <td>{{ personne.statut }}</td>
</tr>

On utilise aussi la directive v-for sur la propriété personnes. Nous verrons les filtres plus loin. Les données des lignes suivent dans les balises td. On remarque l'utilisation du filtre uppercase pour avoir les noms en capitales.

VI-F-3. Le tri

Le tri est assuré par le filtre orderBy :

 
Sélectionnez
orderBy sortKey orderKey

On retrouve les deux arguments dans le modèle :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
data: {
  ...
  sortKey: '',
  orderKey: '',
  ...
},

Il suffit donc de bien renseigner ces propriétés pour assurer le tri. On a vu plus haut qu'on appelle la méthode action avec comme paramètre l'index de la colonne lorsqu'on clique sur l'icône de tri. Voici cette méthode :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
action: function(index) {
  var self = this;
  this.heads.forEach(function(el) {
    if(el.sort != self.heads[index].sort) {
      el.order = 0;
      el.classe = '';
    };
  });
  var info = this.heads[index];     
  if(++info.order == 3) info.order = 1;
  info.classe = classOrder[info.order];
  this.heads.$set(index, info)
  this.orderKey = keyOrder[info.order];
  this.sortKey = info.sort;
}

Cette méthode accomplit un certain nombre d'actions :

  • réinitialise les propriétés order et classe de toutes les autres colonnes pour le cas où on a déjà un tri actif ;
  • incrémente la propriété order de la colonne et la ramène dans l'intervalle 1-2 ;
  • actualise les données du modèle ;
  • assigne la nouvelle valeur des propriétés orderKey et sortKey pour l'affichage.

VI-F-4. Filtrage

Le filtrage est plus simple, on s'en sort élégamment seulement avec les liaisons.

Dans le modèle on a :

 
Sélectionnez
1.
2.
3.
4.
5.
data: {
  ...
  search: '',
  filterKey: 'nom'
},

La propriété search est liée à la zone de texte :

 
Sélectionnez
<input type="text" class="form-control" placeholder="Recherche ici" v-model="search">

Le filtrage est assuré par le filtre filterBy

 
Sélectionnez
filterBy search in filterKey

On change la colonne de filtrage avec les boutons radio :

 
Sélectionnez
1.
2.
3.
4.
5.
<div class="radio" v-for="head in heads">
  <label>
    <input type="radio" value="{{ head.sort }}" v-model="filterKey">{{ head.titre }}
  </label>
</div>

On crée ces boutons radio avec encore la directive v-for qui aide à rendre le HTML plus lisible.

Cet exemple aurait pu être codé de façon différente, mais en l'état, il constitue un bon récapitulatif de ce que nous avons vu jusque là.

VI-G. En résumé

  • Les filtres permettent de modifier les données avant leur affichage.
  • On peut utiliser plusieurs filtres.
  • Il existe un filtre spécifique pour les événements du clavier.
  • Le filtre filterBy permet de filtrer une liste.
  • Le filtre orderBy permet d'ordonner une liste.

VII. Accesseurs et mutateurs

Dans les précédents chapitres, nous avons vu comment stocker des valeurs dans le modèle et comment les lier à la vue. Les filtres permettent d'apporter des modifications avant affichage, mais ils ne portent que sur une donnée spécifique. Il est aussi possible d'effectuer un peu de traitement dans la vue, mais c'est assez limité (concaténation, simple calcul…).

Ici nous allons voir comment intervenir au passage lorsque nous stockons une donnée dans le modèle et lorsque nous l'extrayons.

VII-A. Accesseurs et mutateurs

Ce vocabulaire, accesseurs (getters) et des mutateurs (setters), est issu de la programmation objet pour laquelle les propriétés sont en général privées ou au moins protégées et donc non directement accessibles. Il faut ainsi prévoir des méthodes pour accéder à ces propriétés et les modifier.

On a vu que les propriétés d'un objet de vue.js ne sont pas privées ou protégées et nous y avons directement accès. Par contre la notion d'accesseur et de mutateur va devenir intéressante si nous voulons faire subir des modifications aux données au passage (calcul, mise en forme…).

Voici une petite illustration des deux processus complémentaires :

Image non disponible

VII-A-1. Accesseur

Prenons un exemple simple :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
new Vue({
  el: '#tuto',
  data: {
    responsable: {nom: "Claret", prenom: "Marcel"}
  },
});

On sait qu'on peut afficher facilement ce responsable :

 
Sélectionnez
<div id="tuto">
  <p>Le responsable est {{ responsable.nom }} {{ responsable.prenom }}.</p>
</div>

Avec ce résultat :

Le responsable est Claret Marcel.

Au niveau de mustache on peut très bien effectuer une concaténation :

 
Sélectionnez
<p>Le responsable est {{ responsable.nom + ' ' + responsable.prenom }}.</p>

Et on obtiendra le même rendu.

On peut aussi utiliser la directive v-text au lieu de mustache :

 
Sélectionnez
<p v-text="'Le responsable est ' + responsable.nom + ' ' + responsable.prenom + '.'"></p>

On y perd en lisibilité, mais le résultat est encore identique.

Mais avouez que tout cela n'est pas très élégant. Voyons une autre façon de procéder. Créons une nouvelle propriété :

 
Sélectionnez
<div id="tuto">
  <p>Le responsable est {{ nomComplet }}.</p>
</div>

On veut que cette propriété nomComplet nous donne le nom complet. On va voir une nouveauté dans la VueModèle :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
new Vue({
  el: '#tuto',
  data: {
    responsable: {nom: "Claret", prenom: "Marcel"}
  },
  computed: {
    nomComplet: function() {
      return this.responsable.nom + ' ' + this.responsable.prenom; 
    }    
  }
});

La propriété computed est destinée à la création d'accesseurs comme celui que nous venons de créer.

Voici une schématisation du fonctionnement :

Image non disponible

On utilise cette nouvelle propriété comme les autres.

VII-B. Mutateur

La question qu'on peut se poser maintenant est : est-ce que ça marche dans les deux sens ? Autrement dit si j'utilise ce HTML avec le JavaScript vu ci-dessus :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
<form id="tuto">
  <div class="form-group">
    <input type="texte" class="form-control" v-model="nomComplet">
  </div>
  <p>Le nom est : {{ responsable.nom }}</p>
  <p>Le prénom est : {{ responsable.prenom }}</p>
</form>

Je me retrouve avec une zone de texte et les deux lignes :

Image non disponible

La zone de texte a bien été remplie avec notre nouvelle propriété, mais si on change le contenu de cette zone de texte, on se rend compte évidemment que le modèle n'est pas changé. Il va falloir compléter un peu côté JavaScript :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
new Vue({
  el: '#tuto',
  data: {
    responsable: {nom: "Claret", prenom: "Marcel"}
  },
  computed: {
    nomComplet: {
      get: function () {
        return this.responsable.nom + ' ' + this.responsable.prenom; 
      },
      set: function (value) {
        var nom = value.split(' ');
        this.responsable.nom = nom[0];
        this.responsable.prenom = nom[1];
      }
    }
  }
});

On a décomposé la propriété en deux fonctions :

  • un accesseur (get) pour récupérer les données ;
  • un mutateur (set) pour enregistrer les données.

Maintenant ça fonctionne dans les deux sens :

Image non disponible

Moralité : si vous avez juste besoin d'un accesseur il suffit de prévoir une fonction, si vous avez besoin aussi d'un mutateur il faut prévoir get et set.

VII-C. Un panier

Nous allons prendre un exemple pour illustrer l'utilisation d'un accesseur. Nous voulons réaliser un panier avec des noms de produits, des quantités et des prix. Nous voulons aussi avoir les prix résultants par produit et le montant total. Voici le code complet de la page :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
13.
14.
15.
16.
17.
18.
19.
20.
21.
22.
23.
24.
25.
26.
27.
28.
29.
30.
31.
32.
33.
34.
35.
36.
37.
38.
39.
40.
41.
42.
43.
44.
45.
46.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
60.
61.
62.
63.
64.
65.
66.
67.
68.
69.
70.
71.
72.
73.
74.
75.
76.
77.
78.
79.
80.
81.
82.
83.
84.
85.
86.
87.
88.
89.
90.
91.
92.
93.
94.
95.
96.
97.
98.
99.
100.
<!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">
    <link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.3.0/css/font-awesome.min.css">
  </head>
 
  <body>
 
    <div class="container" id="tuto">
      <br>
 
      <div class="panel panel-primary">
        <div class="panel-heading">Panier</div>        
        <table class="table table-bordered table-striped">
          <thead>
            <tr>
             <th class="col-sm-4">Article</th>
             <th class="col-sm-2">Quantité</th>
             <th class="col-sm-2">Prix</th>
             <th class="col-sm-2">Total</th>
             <th class="col-sm-1"></th>
             <th class="col-sm-1"></th>
            </tr>
          </thead>
          <tbody>
            <tr v-for="item in panier">
              <td>{{ item.article | capitalize }}</td>
              <td>{{ item.quantite }}</td> 
              <td>{{ item.prix | currency '' }}</td>
              <td>{{ item.quantite * item.prix | currency '' }}</td>
              <td><button class="btn btn-info btn-block" v-on:click="modifier($index)"><i class="fa fa-edit fa-lg"></i></button></td>
              <td><button class="btn btn-danger btn-block" v-on:click="supprimer($index)"><i class="fa fa-trash-o fa-lg"></i></button></td>
            </tr> 
            <tr>
              <td colspan="3"></td>
              <td><strong>{{ total | currency '' }}</strong></td>
              <td colspan="2"></td>
            </tr> 
            <tr>
              <td><input type="text" class="form-control" v-model="input.article" v-el:modif placeholder="Article"></td>
              <td><input type="text" class="form-control" v-model="input.quantite" placeholder="Quantité"></td>
              <td><input type="text" class="form-control" v-model="input.prix" placeholder="Prix"></td>
              <td colspan="3"><button class="btn btn-primary btn-block" v-on:click="ajouter()">Ajouter</button></td>
            </tr>
          </tbody>       
        </table>
      </div> 
  
    </div>
 
    <script src="http://cdn.jsdelivr.net/vue/1.0.10/vue.min.js"></script>
 
    <script>
 
      new Vue({
        el: '#tuto',
        data: {
          panier: [
            { article: "cahier", quantite: 2, prix: 5.30 },
            { article: "crayon", quantite: 4, prix: 1.10 },
            { article: "gomme", quantite: 1, prix: 3.25 }
          ],
          input: { article: '', quantite: 0, prix: 0 }
        },
        computed: {
          total: function () {
            var total = 0;
            this.panier.forEach(function(el) {
              total += el.prix * el.quantite;
            });
            return total; 
          }
        },
        methods: {
          ajouter: function() {
            this.panier.push(this.input);
            this.input = { article: '', quantite: 0, prix: 0 };
          },
          modifier: function(index) {
            this.input = this.panier[index];
            this.panier.splice(index, 1);
            this.$$.modif.focus();
          },
          supprimer: function(index) {
            this.panier.splice(index, 1);
          },
        }
      });
 
    </script>
 
  </body>
 
</html>

VII-C-1. Fonctionnement

Au départ nous avons cet aspect :

Image non disponible

Sont déjà présents trois articles avec leur nom, leur prix unitaire, le total par produit et le total du panier. Pour chaque article on dispose d'un bouton de modification et d'un autre pour la suppression. D'autre part on a un formulaire pour ajouter un article.

Lorsqu'on veut modifier un article en cliquant sur le bouton de modification, il disparaît de la liste et se retrouve dans le formulaire :

Image non disponible

Le bouton « Ajouter » permet de le renvoyer dans la liste une fois modifié. L'ajout d'un article se fait juste en utilisant le formulaire.

Dans tous les cas, les montants dans la colonne « Total » s'actualisent.

On peut supprimer un article ou en ajouter un :

Image non disponible

VII-C-2. Le code

Le modèle comporte le tableau panier pour les articles de la liste et l'objet input pour le formulaire :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
data: {
  panier: [
    { article: "cahier", quantite: 2, prix: 5.30 },
    { article: "crayon", quantite: 4, prix: 1.10 },
    { article: "gomme", quantite: 1, prix: 3.25 }
  ],
  input: { article: '', quantite: 0, prix: 0 }
},

La liste est créée avec une boucle v-for :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
<tr v-for="item in panier">
  <td>{{ item.article | capitalize }}</td>
  <td>{{ item.quantite }}</td> 
  <td>{{ item.prix | currency '' }}</td>
  <td>{{ item.quantite * item.prix | currency '' }}</td>
  <td><button class="btn btn-info btn-block" v-on:click="modifier($index)"><i class="fa fa-edit fa-lg"></button></td>
  <td><button class="btn btn-danger btn-block" v-on:click="supprimer($index)"><i class="fa fa-trash-o fa-lg"></i></button></td>
</tr>

Remarquez que le calcul pour le montant par article selon la quantité est effectué directement avec une expression dans mustache. D'autre part on utilise le filtre currency pour ajouter le symbole de la monnaie (par défaut c'est $).

Nous verrons dans un tutoriel ultérieur comment créer un accesseur qui fonctionne dans une boucle avec v-for.

Pour le montant total on a ce code :

 
Sélectionnez
1.
2.
3.
4.
5.
<tr>
  <td colspan="3"></td>
  <td><strong>{{ total | currency '' }}</strong></td>
  <td colspan="2"></td>
</tr>

On voit qu'on référencie la propriété total. C'est une propriété calculée (un accesseur) qu'on retrouve ici :

 
Sélectionnez
1.
2.
3.
4.
5.
6.
7.
8.
9.
computed: {
  total: function () {
    var total = 0;
    this.panier.forEach(function(el) {
      total += el.prix * el.quantite;
    });
    return total; 
  }
},

Le reste du code correspond à des choses que nous avons déjà rencontrées et je vous renvoie aux chapitres précédents si quelque chose ne vous semble pas clair.

VII-D. En résumé

  • Un accesseur permet de récupérer une donnée en la modifiant.
  • Un mutateur permet de mémoriser une donnée en la modifiant.
  • La propriété computed de vue.js permet de créer des accesseurs et des mutateurs.

VIII. Remerciements

Nous remercions Maurice Chavelli qui nous a autorisés à publier ce tutoriel.

Nous tenons également à remercier Winjerome pour la mise au gabarit et Claude Leloup pour la correction orthographique.

Vous avez aimé ce tutoriel ? Alors partagez-le en cliquant sur les boutons suivants : Viadeo Twitter Facebook Share on Google+   

  

Copyright © 2017 Laravel. Aucune reproduction, même partielle, ne peut être faite de ce site et de l'ensemble de son contenu : textes, documents, images, etc. sans l'autorisation expresse de l'auteur. Sinon vous encourez selon la loi jusqu'à trois ans de prison et jusqu'à 300 000 € de dommages et intérêts.