V. Accesseurs et mutateurs▲
Dans les chapitres précédents nous avons vu comment stocker des valeurs dans le modèle et comment les lier à la vue. Dans ce chapitre, nous allons voir comment intervenir au passage lorsque nous stockons une donnée dans le modèle et lorsque nous l'extrayons.
V-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 que 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 :
Dans la documentation de Vue.js, on ne parle pas d'accesseurs et mutateurs mais de propriétés calculées (computed properties).
V-A-1. Accesseur▲
Prenons un exemple simple :
2.
3.
4.
5.
6.
new Vue
({
el
:
'#tuto'
,
data
:
{
responsable
:
{
nom
:
"Claret"
,
prenom
:
"Marcel"
}
},
}
);
On sait qu'on peut afficher facilement ce responsable :
<
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 :
<
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 :
<
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é :
On veut que cette propriété nomComplet nous donne le nom complet. On va voir une nouveauté dans la VueModèle :
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 :
On utilise cette nouvelle propriété comme les autres.
V-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 :
Je me retrouve avec une zone de texte et les deux lignes :
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 :
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 :
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.
V-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 :
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.
<!
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, index) in panier
"
>
<
td>
{{
item.
article }}
<
/td
>
<
td>
{{
item.
quantite }}
<
/td
>
<
td>
{{
item.
prix }}
€<
/td
>
<
td>
{{
(item.
quantite *
item.
prix).
toFixed(2
) }}
€<
/td
>
<
td><button class
=
"
btn btn-info btn-block
"
@click
=
"
modifier(index)
"
><i class
=
"
fa fa-edit fa-lg
"
></i
></button
></td
>
<
td><button class
=
"
btn btn-danger btn-block
"
@click
=
"
supprimer(index)
"
><i class
=
"
fa fa-trash-o fa-lg
"
></i
></button
></td
>
<
/tr
>
<
tr>
<
td colspan
=
"
3
"
></td
>
<
td><strong>
{{
total }}
€<
/strong
></td
>
<
td colspan
=
"
2
"
></td
>
<
/tr
>
<
tr>
<
td><input type
=
"
text
"
class
=
"
form-control
"
v-model
=
"
input.article
"
ref
=
"
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
=
"
https://unpkg.com/vue@2.0.3/dist/vue.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.toFixed
(
2
);
}
},
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
.
$refs.
modif.focus
(
);
},
supprimer
:
function
(
index) {
this
.
panier.splice
(
index,
1
);
},
}
}
);
</
script>
<
/body
>
<
/html
>
V-C-1. Fonctionnement▲
Au départ nous avons cet aspect :
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 :
Le bouton « Ajouter » permet de renvoyer l'article 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 :
V-C-2. Le code▲
Le modèle comporte le tableau panier pour les articles de la liste et l'objet input pour le formulaire :
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 :
2.
3.
4.
5.
6.
7.
8.
<
tr v-for
=
"
(item, index) in panier
"
>
<
td>
{{
item.
article }}
<
/td
>
<
td>
{{
item.
quantite }}
<
/td
>
<
td>
{{
item.
prix }}
€<
/td
>
<
td>
{{
(item.
quantite *
item.
prix).
toFixed(2
) }}
€<
/td
>
<
td><button class
=
"
btn btn-info btn-block
"
@click
=
"
modifier(index)
"
><i class
=
"
fa fa-edit fa-lg
"
></i
></button
></td
>
<
td><button class
=
"
btn btn-danger btn-block
"
@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.
Pour le montant total on a ce code :
On voit qu'on référence la propriété total. C'est une propriété calculée (un accesseur) qu'on retrouve ici :
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.
V-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.