forked from DiscoverMeteor/DiscoverMeteor_fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path09-errors.md.erb
213 lines (157 loc) · 9.87 KB
/
09-errors.md.erb
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
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
---
title: Erreurs
slug: errors
date: 0009/01/01
number: 9
points: 10
photoUrl: http://www.flickr.com/photos/ikewinski/9413892879/
photoAuthor: Mike Lewinski
contents: Créer un meilleur mécanisme pour afficher les erreurs et les messages.|Apprendre à utiliser `Template.rendered` pour savoir quand un utilisateur a vu une erreur.|Utiliser un filtre au niveau du routeur pour s'assurer que les erreurs ont été vues une fois.
paragraphs: 31
---
En utilisant simplement la boite de dialogue de navigateur standard `alert()` pour avertir l'utilisateur quand il y a un problème avec l'envoi de leur formulaire n'est pas très satisfaisant, et ce n'est clairement pas fait pour une bonne UX (User Experience). Nous pouvons faire mieux.
A la place, construisons un mécanisme de rapport d'erreurs plus versatile
qui fera un meilleur travail pour prévenir l'utilisateur de ce qu'il se passe sans casser son processus.
### Présenter les Collections Locales
Nous allons implémenter un simple système qui conservera une trace de quelles erreurs un utilisateur a déjà vues et afficher les nouvelles dans une zone "flash" du site. Ce pattern UX est utile quand nous voulons informer un utilisateur que quelque chose s'est passé sans interrompre son processus de travail.
Ce que nous allons créer est similaire aux messages flash qu'on trouve souvent dans les applications Ruby on Rails, mais c'est plus subtile car c'est implémenté côté client et que l'on sait quand un utilisateur a vu un message.
Pour commencer, nous créons une collection pour stocker nos erreurs. Etant donné que les erreurs sont seulement pertinantes pour la session courante et n'ont pas besoin d'être persistantes dans d'autres cas, nous allons faire quelque chose de nouveau, et créer une _collection locale_. Cela signifie que la collection `Errors` existera seulement dans le navigateur, et ne fera aucune tentative de synchronisation avec le serveur.
Pour terminer ça, nous créons simplement l'erreur dans un fichier client-seulement, avec le nom de collection configuré à `null`. Nous créons une fonction `throwError` qui insère simplement une erreur dans notre nouvelle collection :
~~~js
// Collection Locale (client-seulement)
Errors = new Meteor.Collection(null);
~~~
<%= caption "client/helpers/errors.js" %>
Maintenant que la collection a été créé, vous pouvez ajouter une fonction `throwError` que nous appellerons pour ajouter des erreurs dedans. Nous n'avons pas besoin de nous inquiéter de `allow` ou `deny` ou autre chose de ce type, comme c'est une collection locale et qu'elle ne sera pas sauvée vers la base de données Mongo.
~~~js
throwError = function(message) {
Errors.insert({message: message})
}
~~~
<%= caption "client/helpers/errors.js" %>
L'avantage d'utiliser une collection locale pour stocker les erreurs est que, comme toutes les collections, c'est réactif -- ça veut dire que nous pouvons afficher les erreurs déclarativement de la même façon que nous affichons les données de n'importe quelle autre collection.
### Afficher les erreurs
Nous allons afficher les erreurs en haut de notre layout principal :
~~~html
<template name="layout">
<div class="container">
{{> header}}
{{> errors}}
<div id="main" class="row-fluid">
{{yield}}
</div>
</div>
</template>
~~~
<%= caption "client/views/application/layout.html" %>
<%= highlight "4" %>
Maintenant créons les templates `errors` et `error` dans `errors.html` :
~~~html
<template name="errors">
<div class="errors row-fluid">
{{#each errors}}
{{> error}}
{{/each}}
</div>
</template>
<template name="error">
<div class="alert alert-error">
<button type="button" class="close" data-dismiss="alert">×</button>
{{message}}
</div>
</template>
~~~
<%= caption "client/views/includes/errors.html" %>
<% note do %>
### Templates jumeaux
Vous noterez que nous mettons deux templates dans un fichier simple. Jusqu'à maintenant nous avons essayé d'adhérer à la convention "un fichier, un template", mais tant que Meteor est concerné, mettre tous vos templates dans un fichier simple fonctionne aussi bien (bien que ça pourrait faire un `main.html` très confus).
Dans ce cas, vu que les templates d'erreur sont courts, nous allons faire une exception et les mettre dans le même fichier pour avoir un dépôt un peu plus clair.
<% end %>
Nous avons juste besoin d'intégrer notre helper de template, et nous seront fin prêts !
~~~js
Template.errors.helpers({
errors: function() {
return Errors.find();
}
});
~~~
<%= caption "client/views/includes/errors.js" %>
<%= commit "9-1", "Rapport d'erreur basique." %>
### Créer des erreurs
Nous savons comment afficher des erreurs, mais nous avons encore besoin d'en créer quelques unes avant d'en voir. Les erreurs sont la plupart du temps déclenchées par les utilisateurs entrant un nouveau contenu, donc nous allons vérifier les erreurs dans notre callback de création d'article, et afficher le message pour toutes les erreurs qui surgissent.
En plus, si nous obtenons une erreur `302` (laquelle indique que l'article avec la même URL existe déjà), nous redirigerons l'utilisateur vers l'article existant. Nous obtenons l'`id` de l'article existant depuis `error.details` (souvenez-vous que nous passons l'`id` de cet article comme troisième argument `details` de notre classe `Error` dans le chapitre 7).
~~~js
Template.postSubmit.events({
'submit form': function(e) {
e.preventDefault();
var post = {
url: $(e.target).find('[name=url]').val(),
title: $(e.target).find('[name=title]').val(),
message: $(e.target).find('[name=message]').val()
}
Meteor.call('post', post, function(error, id) {
if (error) {
// Afficher l'erreur à l'utilisateur
throwError(error.reason);
if (error.error === 302)
Router.go('postPage', {_id: error.details})
} else {
Router.go('postPage', {_id: id});
}
});
}
});
~~~
<%= caption "client/views/posts/post_submit.js" %>
<%= highlight "12~14, 16~21" %>
<%= commit "9-2", "Utilisation du rapport d'erreurs." %>
Essayez : tentez de créer un article et entrez une URL `http://meteor.com`. Comme cette URL est déjà attachée à un article dans la préinstallation, vous devriez voir :
<%= screenshot "9-1", "Déclencher une erreur" %>
### Acquitter les erreurs
Si vous cliquez sur le bouton fermer de l'erreur, vous verrez l'erreur disparaître, parce que ce bouton fermer déclenche le JavaScript embarqué de Twitter Bootstrap.
Mais pendant que Bootstrap supprime le `<div>` d'erreur dans le DOM, ça ne supprime pas l'objet erreur de la collection Meteor. Donc ajoutons un moyen de nettoyer notre collection locale une fois que l'erreur a fini son travail.
Premièrement, nous allons modifier la fonction `throwError` pour inclure une propriété `seen`. Ça sera utile plus tard de garder une trace pour savoir si une erreur a été vue par l'utilisateur.
Une fois fait, nous pouvons coder une simple fonction `clearErrors` qui efface ces erreurs "vues".
~~~js
// Collection locale (client-seulement)
Errors = new Meteor.Collection(null);
throwError = function(message) {
Errors.insert({message: message, seen: false})
}
clearErrors = function() {
Errors.remove({seen: true});
}
~~~
<%= caption "client/helpers/errors.js" %>
<%= highlight "5,8~10" %>
Ensuite, nous allons nettoyer les erreurs dans le routeur pour que la navigation d'une page à l'autre fasse disparaître ces erreurs pour toujours :
~~~js
// ...
Router.before(requireLogin, {only: 'postSubmit'})
Router.before(function() { clearErrors() });
~~~
<%= caption "lib/router.js" %>
<%= highlight "4" %>
Afin que notre fonction `clearErrors()` fasse son travail, les erreurs ont besoin d'être marquées comme `seen` (vues). Pour faire ça proprement, il y a un cas particulier que nous devons gérer : quand nous envoyons une erreur et que nous redirigeons l'utilisateur quelque part (comme nous le faisons quand nous essayons de poster un lien dupliqué), la redirection arrive instantanément. Cela signifie que l'utilisateur n'a jamais la chance de voir l'erreur avant qu'elle soit effacée.
C'est ici que notre propriété `seen` va être pratique. Nous avons besoin de nous assurer que c'est seulement mis à `true` si l'utilisateur a vu l'erreur.
Pour terminer ça, nous utiliserons `Meteor.defer()`. Cette fonction dit à Meteor d'exécuter son callback "juste après" quoi qu'il arrive maintenant. Si ça aide, vous pouvez considérer que `defer()` est comme dire au navigateur d'attendre 1 milliseconde avant d'agir'.
Ce que nous faisons est dire à Meteor de mettre `seen` à `true` 1 milliseconde après que le template `errors` soit rendu. Mais vous souvenez-vous comment nous avons dit que la redirection se passe instantanément ? Çela signifie que la redirection se déclenchera avant le callback `defer`, qui n'aura jamais la chance d'être exécuté.
C'est exactement ce que nous voulons : si ce n'est pas exécuté notre erreur ne sera pas marqué comme `seen`, ce qui signifie qu'elle ne sera pas effacée, ce qui signifie qu'elle apparaîtra sur la page vers laquelle notre utilisateur est redirigé et c'est ce que nous voulions !
~~~js
Template.errors.helpers({
errors: function() {
return Errors.find();
}
});
Template.error.rendered = function() {
var error = this.data;
Meteor.defer(function() {
Errors.update(error._id, {$set: {seen: true}});
});
};
~~~
<%= caption "client/views/includes/errors.js" %>
<%= highlight "7~12" %>
<%= commit "9-3", "Monitorer quelles erreurs ont été vues, et effacer en routant." %>
Le callback `rendered` se déclenche une fois que notre template a été rendu dans le navigateur. Dans ce callback, `this` refère à l'instance de template courante, et `this.data` nous laisse accéder aux données de l'objet qui est rendu (dans notre cas, une erreur).
Whew ! C'était beaucoup de travail pour quelque chose que les utilisateurs espérons-le ne verront jamais !