forked from DiscoverMeteor/DiscoverMeteor_fr
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy path12-pagination.md.erb
467 lines (343 loc) · 23.8 KB
/
12-pagination.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
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
---
title: La Pagination
slug: pagination
date: 0012/01/01
number: 12
points: 10
photoUrl: http://www.flickr.com/photos/ikewinski/8625379401/
photoAuthor: Mike Lewinski
contents: En apprendre plus sur les abonnements de Meteor, et comment nous pouvons les utiliser pour contrôler les données.|Implémenter la pagination style infini.|Utiliser le paquet `iron-router-progress` pour implémenter une barre de changement type IOS.|Créer un abonnement spécial pour traiter des liens directs vers les pages d'articles.
paragraphs: 67
---
Les choses semblent bien avec Microscope, et nous pouvons nous attendre à un succès quand il va sortir pour tout le monde.
Donc nous devrions probablement réfléchir un peu à propos de la conséquence sur les performances du nombre de nouveaux articles qui vont être entrés dans le site au moment de son envol !
Nous avons précédemment parlé de comment une collection côté client peut contenir un sous-ensemble des données du serveur, et nous avons même appris à réaliser cela pour nos collections de notifications et de commentaires.
À présent, nous publions encore tous nos articles d'un seul coup, à tous les utilisateurs connectés. Éventuellement, si des milliers de liens sont postés, ceci deviendra problématique. Pour résoudre cela, nous avons besoin de paginer nos articles.
### Ajouter plus d'articles
Premièrement, dans nos données de préinstallation, chargeons assez d'articles pour que cette pagination ait un sens :
~~~js
// Fixture data
if (Posts.find().count() === 0) {
//...
Posts.insert({
title: 'The Meteor Book',
userId: tom._id,
author: tom.profile.name,
url: 'http://themeteorbook.com',
submitted: now - 12 * 3600 * 1000,
commentsCount: 0
});
for (var i = 0; i < 10; i++) {
Posts.insert({
title: 'Test post #' + i,
author: sacha.profile.name,
userId: sacha._id,
url: 'http://google.com/?q=test-' + i,
submitted: now - i * 3600 * 1000,
commentsCount: 0
});
}
}
~~~
<%= caption "server/fixtures.js" %>
<%= highlight "15~24" %>
Après avoir exécuté `meteor reset`, vous devriez obtenir quelque chose comme ça :
<%= screenshot "12-1", "Afficher des données factices." %>
<%= commit "12-1", "Ajout d'assez d'articles pour que la pagination soit nécessaire." %>
### Pagination infinie
Nous allons implémenter une pagination de style "infini". Ce que nous voulons dire par là c'est que nous voulons premièrement afficher, disons, 10 articles à l'écran, avec un lien "charger plus d'articles" inscrit en bas. Cliquer sur le lien affichera 10 articles supplémentaires dans la liste, et ainsi de suite *ad infinitum*. Cela signifie que nous pouvons contrôler notre système de pagination entier avec un simple paramètre représentant le nombre d'articles à afficher à l'écran.
Maintenant nous allons avoir besoin d'un moyen d'indiquer au serveur ce paramètre afin qu'il sache combien d'articles envoyer au client. Il se trouve que nous nous abonnons déjà à la publication `posts` dans le routeur, donc nous allons profiter de ça et laisser le routeur gérer notre pagination.
La façon la plus facile de configurer cela est simplement de faire du paramètre de limitation d'articles partie intégrante du chemin, nous donnant des URLs de la forme `http://localhost:3000/25`. Un autre bon côté d'utiliser l'URL plutôt que d'autres méthodes est que si vous affichez 25 articles et que vous recharger la page dans le navigateur par erreur, vous verrez toujours 25 articles une fois la page chargée.
Afin de faire ça prroprement, nous allons avoir besoin de changer la façon dont nous nous abonnons aux articles. Juste comme nous l'avons fait dans le chapitre *Commentaires*, nous allons avoir besoin de déplacer notre code de l'abonnement du niveau *routeur* au niveau *route*.
Tout ça est peut-être un peu beaucoup à comprendre en une seule fois, mais cela deviendra plus clair avec le code.
Premièrement, nous arrêterons l'abonnement à la publication `posts` dans le bloc `Routeur.configure()`. Supprimez juste `Meteor.suscribe('posts')`, en laissant seulement l'abonnement aux `notifications` :
~~~js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
waitOn: function() {
return [Meteor.subscribe('notifications')]
}
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "5" %>
Nous ajouterons un paramètre `postsLimit` au chemin de la route. Ajouter un `?` après le nom du paramètre signifie que c'est optionnel. Donc cette route ne correspondra pas seulement à `http://localhost:3000/50`, mais également au simple et ancien `http://localhost:3000`.
~~~js
Router.map(function() {
//...
this.route('postsList', {
path: '/:postsLimit?'
});
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "5" %>
C'est important de noter qu'un chemin de la forme `/:parameter?` correspondra à tous les chemins possibles. Chaque route sera analysée successivement pour voir si elle correspond avec le chemin courant, nous avons besoin de nous assurer que nous organisons nos routes dans un ordre de spécificité décroissante.
En d'autres mots, les routes qui ciblent des routes plus spécifiques comme `/posts/:_id` viendraient en première, et notre route `postsList` seraient déplacées en bas du fichier vu qu'elle correspond avec tout.
Il est maintenant temps d'aborder le dur problème de s'abonner et trouver les bonnes données. Nous avons besoin de gérer le cas où le paramètre `postsLimit` n'est pas présent, donc nous l'assignerons à une valeur par défaut. Nous utiliserons "5" pour nous donner vraiment assez de place pour jouer avec les paginations.
~~~js
Router.map(function() {
//..
this.route('postsList', {
path: '/:postsLimit?',
waitOn: function() {
var postsLimit = parseInt(this.params.postsLimit) || 5;
return Meteor.subscribe('posts', {sort: {submitted: -1}, limit: postsLimit});
}
});
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "6~9" %>
Vous noterez que nous passons maintenant un objet JavaScript ({limit: postsLimit}) avec le nom de notre publication `posts`. Cet objet servira au même titre que le paramètre `options` pour l'expression de `Posts.find()` côté serveur. Faisons quelques modifications dans notre code côté serveur pour immplémenter ça :
~~~js
Meteor.publish('posts', function(options) {
return Posts.find({}, options);
});
Meteor.publish('comments', function(postId) {
return Comments.find({postId: postId});
});
Meteor.publish('notifications', function() {
return Notifications.find({userId: this.userId});
});
~~~
<%= caption "server/publications.js" %>
<%= highlight "1~3" %>
<% note do %>
### Passer des paramètres
Notre code de publications est effectivement en train de dire au serveur qu'il peut faire confiance à tout objet JavaScript envoyé par le client (dans notre cas, `{limit: postsLimit}`) pour servir comme les `options` d'expression de `find()`. Ceci rend possible pour les utilisateurs de soumettre des options qu'ils aiment via la console du navigateur.
Dans notre cas, c'est relativement anodin, vu que tout ce qu'un utilisateur peut faire est réordonner les articles différemment, ou changer la limite (ce que nous voulons mettre en place en premier lieu).
Mais vous ne devriez pas utiliser ce pattern quand vous stockez des données privées sur des champs non publiés, vu que l'utilisateur pourrait manipuler l'option `fields` pour y accéder, et vous devriez probablement éviter également de l'utiliser pour l'argument de sélecteur de l'expression de `find()` pour les mêmes raisons de sécurité.
Un pattern plus sécurisé pourrait être de passer les paramètres individuels eux-mêmes au lieu de l'objet entier, pour nous assurer que nous gardons le contrôle de nos données.
~~~js
Meteor.publish('posts', function(sort, limit) {
return Posts.find({}, {sort: sort, limit: limit});
});
~~~
<% end %>
Maintenant que nous nous sommes abonnés au niveau de la route, cela aurait du sens de mettre le contexte de données au même endroit. Nous dévierons un peu de notre précédent pattern et feront en sorte que la fonction `data` retourne un objet JavaScript au lieu de simplement retourner un curseur. Ceci nous laisse créer un contexte de données *nommé*, que nous appelerons `posts`.
Ce que cela signifie est simplement qu'au lieu d'être implicitement disponible comme `this` à l'intérieur du template, notre contexte de données sera disponible à `posts`. A part ce petit élément, le code devrait être familier :
~~~js
Router.map(function() {
this.route('postsList', {
path: '/:postsLimit?',
waitOn: function() {
var limit = parseInt(this.params.postsLimit) || 5;
return Meteor.subscribe('posts', {sort: {submitted: -1}, limit: limit});
},
data: function() {
var limit = parseInt(this.params.postsLimit) || 5;
return {
posts: Posts.find({}, {sort: {submitted: -1}, limit: limit})
};
}
});
//..
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "8~13" %>
Maintenant que nous avons mis le contexte de données au niveau du routeur nous pouvons nous débarrasser sans problème du template helper `posts` dans le fichier `posts_list.js`. Et comme nous avons nommé notre contexte de données `posts` (le même nom que le helper), nous n'avons même pas besoin de toucher au template `postsList` !
Récapitulons, Voici à quoi notre nouveau code amélioré de `router.js` ressemble :
~~~js
Router.configure({
layoutTemplate: 'layout',
loadingTemplate: 'loading',
waitOn: function() {
return [Meteor.subscribe('notifications')]
}
});
Router.map(function() {
//...
this.route('postsList', {
path: '/:postsLimit?',
waitOn: function() {
var limit = parseInt(this.params.postsLimit) || 5;
return Meteor.subscribe('posts', {sort: {submitted: -1}, limit: limit});
},
data: function() {
var limit = parseInt(this.params.postsLimit) || 5;
return {
posts: Posts.find({}, {sort: {submitted: -1}, limit: limit})
};
}
});
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "5, 11~21" %>
<%= commit "12-2", "Améliorer la route postsList pour avoir une limite." %>
Essayons notre tout nouveau système de pagination. Nous avons maintenant la possibilité d'afficher un nombre arbitraire d'articles sur la page d'accueil simplement en changeant le paramètre dans l'URL. Par exemple, essayez d'accéder à `http://localhost:3000/3`. Vous verrez maintenant quelque chose comme ça :
<%= screenshot "12-2", "Contrôler le nombre d'articles sur la page d'accueil. " %>
<% note do %>
### Pourquoi pas des pages ?
Pourquoi utilisons-nous une approche de "pagination infinie" au lieu de montrer des pages successives avec 10 articles sur chaque, comme ce que Google fait pour ses résultats de recherche ? C'est actuellement dû au paradigme temps-réel embrassé par Meteor.
Imaginons que nous paginons notre collection `Posts` en utilisant le pattern de pagination de Google, et que nous sommes à la page 2, qui affiche les articles 10 à 20. Que se passe-t-il si un utilisateur supprime un des 10 précédents articles ?
Étant donné que notre application est temps-réel, notre ensemble de données pourrait changer. L'article 10 deviendrait l'article 9, et disparaîtrait de notre vue, pendant que l'article 11 serait maintenant dans le créneau. Le résultat final serait que l'utilisateur verrait soudainement ses articles changer sans raisons apparentes.
Même si nous tolérions cette bizarrerie dans l'expérience utilisateur, la pagination traditionnelle est également difficile à implémenter pour des raisons techniques.
Revenons à notre exemple précédent. Nous publions les articles 10 à 20 de la collection `Posts`, mais comment trouver ces articles sur le client ? Vous ne pouvez pas prendre les articles de 10 à 20, comme il y a seulement dix articles en tout dans l'ensemble de données côté client.
Une solution serait simplement de publier ces 10 articles sur le serveur, et ensuite faire un `Posts.find()` côté client pour récupérer *tous* les articles publiés.
Ceci fonctionne si vous avez seulement un abonnement. Mais que va-t-il se passer si vous avez plus d'un abonnement aux articles, comme nous le ferons bientôt ?
Disons qu'un abonnement demande les articles 10 à 20, et un autre les articles 30 à 40. Vous avez maintenant 20 articles chargés côté client au total, avec aucun moyen de savoir lesquels appartiennent à quel abonnement.
Pour toutes ces raisons, la pagination traditionnelle n'a pas beaucoup de sens lorsqu'on travaille avec Meteor.
<% end %>
### Créer un controlleur de route
Vous avez noté que nous répétons la ligne `var limit = parseInt(this.params.postsLimit) || 5;` deux fois. De plus, coder en dur le nombre "5" n'est pas vraiment idéal. Ce n'est pas la fin du monde, mais comme c'est toujours mieux de suivre le principe DRY (Don't Repeat Yourself) si vous le pouvez, voyons comment nous pouvons remanier un petit peu les choses.
Nous allons présenter un nouvel aspect de Iron Router, les *Controlleurs de Route*. Un controlleur de route est simplement un moyen de grouper les fonctionnalités de routage ensemble dans un paquet réutilisable dont toutes les routes peuvent hériter. Maintenant nous allons l'utiliser uniquement pour une seule route, mais vous verrez dans le prochain chapitre comment cette fonctionnalité deviendra pratique.
~~~js
PostsListController = RouteController.extend({
template: 'postsList',
increment: 5,
limit: function() {
return parseInt(this.params.postsLimit) || this.increment;
},
findOptions: function() {
return {sort: {submitted: -1}, limit: this.limit()};
},
waitOn: function() {
return Meteor.subscribe('posts', this.findOptions());
},
data: function() {
return {posts: Posts.find({}, this.findOptions())};
}
});
Router.map(function() {
//...
this.route('postsList', {
path: '/:postsLimit?',
controller: PostsListController
});
});
~~~
<%= caption "lib/router.js" %>
Voyons les étapes. Premièrement, nous créons notre controlleur en étendant `RouteController`. Puis Nous mettons la propriété `template` juste comme nous l'avons fait avant, et ensuite une nouvelle propriété `increment`.
Puis nous définissions une nouvelle fonction `limit` qui retournera la limite courante, et une fonction `findOptions` qui retournera un objet options. Cela pourrait ressembler à une étape supplémentaire, mais nous en ferons usage plus tard.
Après, nous définissions des fonctions `waitOn` et `data` juste comme avant, excepté que maintenant elles vont utiliser notre nouvelle fonction `findOptions`.
La dernière chose à faire est d'indiquer la route `postsList` pour router vers notre tout nouveau controlleur, avec la propriété `controller`.
<%= commit "12-3", "Route postsList remaniées dans un controlleur de route." %>
### Ajouter un lien Charger plus
Nous avons une pagination qui fonctionne, et notre code est bien fait. Il y a juste un problème : il n'y a aucun moyen d'utiliser actuellement cette pagination excepté en changeant l'URL manuellement. Ceci ne fait définitivement pas une bonne expérience utilisateur, donc retournons au travail pour corriger ça.
Ce que nous voulons faire est assez simple. Nous allons ajouter un bouton "Charger plus" en bas de notre liste d'articles, qui incrémentera le nombre d'articles affichés par 5 chaque fois qu'on clique dessus. Donc si je suis actuellement sur l'URL `http://localhost:3000/5`, cliquer sur "Charger plus" nous amène à `http://localhost:3000/10`. Si vous êtes arrivé aussi loin dans le livre, nous pensons que vous pouvez supporter un peu d'arithmétique.
Comme précédemment, nous ajouterons notre logique de pagination dans notre route. Vous vous souvenez quand nous avons explicitement nommé notre contexte de données plutôt que juste utiliser un curseur anonyme ? Bien, il n'y a pas de règle qui dit que la fonction `data` peut seulement passer des curseurs, donc nous utiliserons la même technique pour générer l'URL de notre bouton "charger plus".
~~~js
PostsListController = RouteController.extend({
template: 'postsList',
increment: 5,
limit: function() {
return parseInt(this.params.postsLimit) || this.increment;
},
findOptions: function() {
return {sort: {submitted: -1}, limit: this.limit()};
},
waitOn: function() {
return Meteor.subscribe('posts', this.findOptions());
},
posts: function() {
return Posts.find({}, this.findOptions());
},
data: function() {
var hasMore = this.posts().fetch().length === this.limit();
var nextPath = this.route.path({postsLimit: this.limit() + this.increment});
return {
posts: this.posts(),
nextPath: hasMore ? nextPath : null
};
}
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "16~23" %>
Regardons plus en détail la magie du routeur. Vous vous souvenez que la route `postsList` (qui hérite du controlleur `PostsListController` sur lequel nous travaillons) prend un paramètre `postsLimit`.
Donc quand nous alimentons `this.route.path()` avec `{postsLimit: this.limit() + this.increment}`, nous disons à la route `postsList` de construire son propre chemin en utilisant cet objet JavaScript comme contexte de données.
En d'autres mots, c'est exactement la même chose qu'utiliser le helper Spacebars `{{pathFor 'postsList'}}`, excepté que nous remplaçons le `this` implicite par notre contexte de données personnalisé.
Nous prenons ce chemin et l'ajoutons au contexte de données pour notre template, mais *seulement* s'il y a plus d'articles à afficher. La manière dont on fait ça est un peu rusée.
Nous savons que `this.limit()` retourne le nombre courant d'articles que nous aimerions montrer, qui peut être la valeur dans l'URL courante, ou notre valeur par défaut (5) si l'URL ne contient pas de paramètre.
D'un autre côté, `this.posts` réfère au curseur courant, donc `this.posts.count()` réfère au nombre d'articles qui sont actuellement dans le curseur.
Donc ce que nous disons ici est que si nous demandons `n` articles et nous récupérons `n`, nous continuerons d'afficher le bouton "charger plus". Mais si nous demandons `n` et que nous récupérons moins de `n`, ça voudra dire que nous avons atteint la limite et que nous voulons arrêter d'afficher ce bouton.
Ceci étant dit, notre système échoue dans un cas ; quand le nombre d'items dans notre base de données est *exactement* `n`. Si cela arrive, le client demandera `n` articles et récupérera `n` articles et continuera à afficher le bouton "charger plus", inconscient qu'il n'y a plus d'items restants.
C'est triste, il n'y a pas de contournements simples à ce problème, donc pour l'instant nous devrons nous contenter de cette implémentation moins-que-parfaite.
Tout ce qu'il reste à faire est d'ajouter le lien "charger plus" en bas de notre liste d'articles, en nous assurant de l'afficher seulement si nous avons encore des articles à charger :
~~~html
<template name="postsList">
<div class="posts">
{{#each posts}}
{{> postItem}}
{{/each}}
{{#if nextPath}}
<a class="load-more" href="{{nextPath}}">Load more</a>
{{/if}}
</div>
</template>
~~~
<%= caption "client/views/posts/posts_list.html" %>
<%= highlight "7~10" %>
Voici à quoi votre liste d'articles devrait ressembler :
<%= screenshot "12-3", "Le bouton “Charger plus“. " %>
<%= commit "12-4", "nextPath() ajouté au controlleur et utilisé pour franchir les articles." %>
### Une meilleure barre de chargement
Notre pagination fonctionne maintenant correctement, mais elle souffre d'une ennuyante bizarrerie : à chaque fois que nous cliquons sur “charger plus” et que le routeur demande plus d'articles, nous sommes envoyés vers le template `loading` pendant que nous attendons l'arrivée des nouvelles données. Le résultat est que nous sommes envoyés en haut de la page à chaque fois et nous avons besoin de faire défiler la page vers le bas pour continuer notre navigation.
Ça serait bien, bien mieux si on pouvait rester sur la même page tout au long de l'opération, tout en fournissant l'information que des nouvelles données sont en cours de chargement. Heureusement, c'est précisément ce que le paquet `iron-router-progress` fait.
Similaire à Safari d'IOS ou des sites comme Medium et Youtube, `iron-router-progress` ajout une petite barre de chargement en haut de l'écran. L'implémenter est aussi facile que d'ajouter le paquet dans votre application :
~~~bash
mrt add iron-router-progress
~~~
<%= caption "Console bash" %>
A travers la magie des paquets intelligents, notre nouvel indicateur de chargement fonctionnera parfaitement dès son installation ! La barre de chargement s'activera pour chaque route, et se complètera automatiquement une fois que les données requises pour la route seront finies de charger.
Nous ferons juste une petite optimisation. Désactivons `iron-router-progress` pour la route `postSubmit` vu que nous n'avons pas besoin d'attendre des données d'abonnement (après tout, c'est juste un formulaire vide) :
~~~js
Router.map(function() {
//...
this.route('postSubmit', {
path: '/submit',
disableProgress: true
});
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "7" %>
<%= commit "12-5", "Utiliser le paquet iron-router-progress pour rendre la pagination plus sympathique." %>
### Accéder à des articles
Nous sommes en train de charger les cinq articles les plus récents par défaut, mais que se passe-t-il quand quelqu'un explore une page d'article individuelle ?
<%= screenshot "12-4", "Un template vide." %>
Si vous essayez, vous allez faire face à un template d'article vide. Cela semble normal : nous avons dit au routeur de s'abonner à la publication `posts` quand il charge la route `postsList`, mais nous ne lui avons pas dit quoi faire au sujet de la route `postPage`.
Mais, tout ce que nous savons faire c'est nous abonner à une liste des `n` derniers articles. Comment demande-t-on au serveur pour un seul article spécifique ? Nous allons vous donner un petit secret ici : vous pouvez avoir plus d'une publication pour chaque collection !
Donc pour retrouver nos articles manquants, nous allons seulement créer une nouvelle, publication `singlePost` séparée qui publie seulement un article, identifié par son `_id`.
~~~js
Meteor.publish('posts', function(options) {
return Posts.find({}, options);
});
Meteor.publish('singlePost', function(id) {
return id && Posts.find(id);
});
~~~
<%= caption "server/publications.js" %>
<%= highlight "5~7" %>
Maintenant, abonnons-nous aux bon articles côté client. Nous nous sommes déjà abonnés à la publication `comments` sur la fonction `waitOn` de la route `postPage`, donc nous pouvons simplement ajouter l'abonnement à `singlePost` ici. Et n'oublions pas d'ajouter également notre abonnement à la route `postEdit`, vu qu'elle nécessite aussi les mêmes données :
~~~js
Router.map(function() {
//...
this.route('postPage', {
path: '/posts/:_id',
waitOn: function() {
return [
Meteor.subscribe('singlePost', this.params._id),
Meteor.subscribe('comments', this.params._id)
];
},
data: function() { return Posts.findOne(this.params._id); }
});
this.route('postEdit', {
path: '/posts/:_id/edit',
waitOn: function() {
return Meteor.subscribe('singlePost', this.params._id);
},
data: function() { return Posts.findOne(this.params._id); }
});
/...
});
~~~
<%= caption "lib/router.js" %>
<%= highlight "7~12,18~20" %>
<%= commit "12-6","Utiliser un abonnement à un unique article pour nous assurer que nous pouvons toujours voir le bon article." %>
Avec la pagination activée, notre application ne souffre plus de problèmes de montée en charge, et les utilisateurs sont sûres de contribuer avec même plus de liens qu'avant. Donc ne serait-il pas super de pouvoir d'une manière ou d'une autre classer ces liens ? C'est précisément l'objet du chapitre suivant, *Voter*.