Vue - Simplonline
Transcription
Vue - Simplonline
DISCOVER METEOR Building Real-Time JavaScript Web Apps Tom Coleman & Sacha Greif Cover photo credit : , Perseid Hunting by Darren Blackburn Commons Attribution . 2.0 licensed under a Creative Generic license . . www discovermeteor com Introduction 1 . Commençons par un petit exercice mental ê deux fen é tres diff rentes de votre ordinateur ê Cliquez maintenant dans une des deux fen ' ê disparu de l autre fen ' é Il n est pas n ê Imaginez que vous ouvriez le m tre ? é cessaire de le faire r . tres et effacez un fichier -- Ce fichier a t il aussi ' . ' è , me de fichier local ' ). Quand on le changement s applique partout ( sans avoir besoin de rafra chir ou d utiliser une fonction de rappel . . ellement pour savoir que c est bien le cas modifie quelque chose dans notre syst î me dossier dans callback Ça marche , tout simplement Par contre , ê imaginons ce m ouvriez le m que vous cr ê et m ê ' é me sc . nario sur le web é Consid rons par exemple que vous ê me panneau d administration Wordpress dans deux fen é ' ' . iez un nouveau post dans l une d elles ' , me si vous attendez é Nous nous sommes habitu , que par de courtes actions , Heureusement ê l autre fen ' s d ann ' , tres de navigateur é Contrairement à l exp rience pr î é c , dente . tre ne changera pas à moins de la rafra chir é é es en ann ' é es à l id ' e qu on ne communique avec un site . distinctes les unes des autres ' Meteor fait partie d une nouvelle vague de frameworks et de technologies qui -é ( - comptent bien remettre en question ce statu quo en rendant le web temps r r éé puis . el ) real time et actif Qu'est-ce que Meteor ? - é Meteor est une plate forme bas ' . é e sur Node js pour cr é C est ce qui fait le lien entre la base de donn , utilisateur é . er des applications web temps r es de votre application et son interface é. tout en assurant que les deux restent bien synchronis é Puisque Meteor est bas Autre chose el , . , sur Node js s ôé il utilise JavaScript à la fois c t ôé client et c t . serveur Meteor permet aussi de partager du code entre les deux environnements . - ê Tout cela nous donne une plate forme pouvant ' tre tr è s puissante et tr fournissant une couche d abstraction à de nombreux probl ' é d è è mes et pi è s simple en ges rencontr é s lors du . veloppement d applications web Pourquoi Meteor ? ' ô Bon alors pourquoi passer votre temps à apprendre Meteor plut é Sans compter les nombreuses fonctionnalit à apprendre é Compar ' ? la raison est claire : Meteor est facile . - , aux autres plate formes application web temps r é d exp , s de Meteor - t qu une autre plate forme rience avec le d ' é ' ' Meteor permet l impl é - , veloppement front end -ê ' . - é tre la plate forme id ' Puisqu il ne faut qu une soir ale qui r - é ê vous é ' é mentation et le d el en l espace de seulement quelques heures vous n aurez pas à apprendre un nouveau langage Meteor est peut é ploiement d une Et si vous avez un peu é tes d jà familier avec le JavaScript et . , pond à tous vos besoins é e ou un week end pour d , marrer -ê mais peut . tre pas pourquoi ne pas essayer ? Pourquoi ce livre ? è Sur les deux derni res ann é , es é nous avons travaill avons fait des applications webs et mobiles , Nous avons beaucoup appris è tr . open source et commerciales é mais trouver les r è s simple , Il nous a fallu assembler les pi . me inventer nos propres solutions é ces leçons et cr ' er un guide simple et d construction d une application Meteor é taill é , Avec ce livre . nouvelles tels que Hacker News ou Reddit , construirons re , et qui vous aiderait à travers toutes les é tapes de la . ' è , tait pas toujours nous souhaitions partager toutes é L application que nous allons construire est une version simplifi avec son grand fr Nous . 'é ponses à nos questions n . ces du puzzle à partir de sources multiples ê souvent m sur de nombreux projets Meteor ' Nous l avons appel é e des sites sociaux de Microscope ). Telescope un autre projet Meteor open source nous vous apprendrons tout les éé l é ments n , application Meteor comme la gestion des utilisateurs ( par analogie Pendant que nous le é cessaires au d ' veloppement d une , les collections Meteor le routing et . beaucoup plus À qui ce livre est-il destiné ? Un de nos objectifs en vous devriez é tre capable de suivre le cours sans exp , , . JavaScript . crivant ce livre est de garder les choses simples et accessibles ê frameworks MVC En revanche é ôé ou encore de la programmation c é Mais si vous avez d , veloppeur du navigateur jà exp t é alable de Meteor . , Node js des é é . serveur en g n l ral mentaire de la syntaxe et des concepts du é é é riment un peu de code jQuery ou jou è vous ne devriez pas avoir de probl me œ il à JavaScript primer for Meteor ( ) en anglais avec la console . , Si vous ne vous sentez pas au point avec JavaScript pour le moment jeter un , éé on attend un connaissance é d rience pr , Ainsi é nous vous sugg avant de revenir sur ce livre rons de . À propos des auteurs ù Au cas o vous vous demandez qui nous sommes et pourquoi vous pouvez nous faire confiance , ' . voici un peu d information sur nous , é Tom Coleman fait partie de Percolate Studio é qualit ' é et l exp . rience utilisateur pour vos applications Meteor ( Meteor open source . Enfin , ' , galement sur Atmosphere ' è c est aussi l un des cerveaux derri ). é un service de paquet re de nombreux projets avec des startups comme Hipmunk et RubyMotion en tant que . concepteur produit et concepteur web ), veloppement web ciblant la par exemple Iron Router Sacha Greif a travaill Telescope é Il travaille un studio de d Il est le cr é ateur de Telescope et de Sidebar . ( é bas sur et il est aussi le fondateur de Folyo Chapitres & Apartés ’ Nous voulions que ce livre soit utile aussi bien pour l utilisateur novice de Meteor que pour le é, programmeur avanc normaux ( é num rot é é ainsi nous avons s s de 1 14 à ) par é et les apart s é ( é les chapitres en deux cat num é ros ) .5 . gories : les chapitres ’ , Les chapitres normaux constituent la ligne conductrice pour construire l application essayerons de la rendre op é è rationnelle d s que possible en expliquant les é importantes sans vous encombrer de d ’ D un autre c ô é, les apart , Ainsi si vous . é s iront plus loin dans les subtilit mieux comprendre ce qui se passe r ê é tes d , butant tapes les plus tails é t é et nous s de Meteor é , et vous aideront à . ellement dans les coulisses é vous pouvez passer les apart revenir à elles une fois que vous aurez jou é è s pour votre premi un peu avec Meteor re lecture , et . Commits & Instances en ligne ’ Il n y a rien de pire que de suivre un livre de programmation et de tout à coup se rendre ’ é compte que le code n est plus synchronis é . comme pr Pour é avec les exemples et que plus rien ne fonctionne vu , viter cela éé nous avons cr é ô un d p , t sur GitHub pour Microscope é et nous proposerons galement des liens directs vers les commits de Git à chaque modification de code chaque commit est li é ’ , à une instance en ligne de l application . avec votre copie locale ' ’ Lancer l instance ce n est pas parce que nous mettons à votre disposition ces commits que ’ . vous devez juste passer d un checkout au suivant é temps n te ' Voir sur GitHub , : ê . Afficher les notifications dans l ent Une seule chose , De plus ainsi vous pourrez le comparer Ici un exemple de ce que vous pourriez voir Commit 11-2 . cessaire pour é ’ Vous apprendrez mieux si vous prenez le crire le code de l application Quelques ressources supplémentaires ! Si jamais vous souhaitez en apprendre plus sur un point pr é cis de Meteor officielle de Meteor est le meilleur endroit pour commencer Nous vous recommandons , questions é # la documentation . galement Stack Overflow pour la r ainsi que le canal IRC , ’ é solution de probl è mes et les . meteor si vous avez besoin d aide en direct Ai-je besoin de Git ? ' é Il n est pas n , livre cessaire d 'ê é tre familiaris avec le contr ô le de versions Git pour suivre ce . mais nous le recommandons fortement é . Si vous souhaitez passer à la vitesse sup rieure , nous vous recommandons Git Is Simpler Than You Think de Nick Farina Si vous ( ê tes un d ) Mac OS é butant , nous vous recommandons é qui vous permet de g ( SourceTree Mac OS & é ô rer vos d ), Windows p é ’ galement l application GitHub ts sans utiliser la ligne de commande tous deux gratuits , ou . Nous contacter , Si vous souhaitez nous contacter @ hello ê De m . vous pouvez nous envoyer un email à . discovermeteor com me , si vous trouvez une erreur typographique ou toute autre erreur dans le , contenu du livre é ô vous pouvez nous en informer en soumettant un bug dans ce d p t . GitHub è Si vous avez un quelconque probl soumettre un bug dans le d , Enfin é ô p me avec le code de Microscope , vous pouvez . t de Microscope pour toute autre question vous pouvez aussi nous laisser un commentaire dans le panneau lat é . ral de cette application Premiers pas è La premi 2 , re impression est importante ' é. l installation de Meteor devrait se faire sans difficult . Vous serez en selle en moins de cinq minutes dans la plupart des cas Pour commencer , si vous terminal et en saisissant ê tes sur Mac OS ou Linux vous pouvez installer Meteor en ouvrant un : curl https://install.meteor.com | sh Si vous ê r ' Ne pas é diatement ' . é cutable meteor è sur votre syst è , me — ou ne souhaitez pas — . , , et vous permettra de installer Meteor localement sur votre ' . . nous vous invitons à examiner Nitrous io en guise d alternative ' é Nitrous io est un service qui vous permet d ex code me installer Meteor Si vous ne pouvez pas syst . rez vous aux instructions d installation du site de Meteor f Cette commande installera l ex l utiliser imm ' - , éé tes sur Windows directement dans votre navigateur œ vous aider à le mettre en . Nous avons r , é é dig 'é et d diter leur un petit guide pour . uvre ' Suivez simplement ce guide jusqu à la section revenez dans ce chapitre , cuter vos applications à la section « Cr é “ Installing Meteor ” , comprise er une application basique ». puis Créer une application basique Maintenant que Meteor est install ligne de commande meteor : é, é cr . ons une application , Pour ce faire ' on utilise l outil en meteor create microscope Cette commande t ' . l emploi ' éé l , chargera Meteor , Lorsqu elle aura abouti contenu suivant puis é tablira un projet Meteor vous devriez trouver un dossier éé l mentaire microscope/ , ê pr t à avec le : .meteor microscope.css microscope.html microscope.js ' éé L application que Meteor a cr é cependant d œ uvre quelques patterns courants ê M ' jà en e pour vous est une simple me si notre application ne fait pas grand chose , l application , é ( bauche ), . boilerplate é nous pouvons d retournez à la ligne de commande et saisissez é jà la d qui met marrer . Pour lancer : cd microscope meteor ê Ouvrez une fen ' tre de navigateur à l adresse http://localhost:3000/ ) http://0.0.0.0:3000/ , ce qui est équivalent ressemble à : ( ' ou à l adresse et vous devriez voir quelque chose qui Le ‹ Hello World › de Meteor. Commit 2-1 é Cr e un projet Microscope Voir sur GitHub é F licitations ê arr ' ! . mentaire ' è , ter l application ' . re application Meteor , Au passage pour ê il vous suffit de ramener au premier plan la fen , tre de terminal dans et de presser la combinaison de touches , N oubliez pas que si vous utilisez Git init . l Lancer l instance Vous avez mis en service votre premi laquelle tourne l application ' éé ' ctrl+c . c est le bon moment pour initialiser votre repo avec git Bye Bye Meteorite ù 0.9.0 Il fut un temps o Meteor Depuis la version é é int gr é tait reli de Meteor , . é à un gestionnaire de paquet nomm ' é Meteorite n est plus n é Meteorite ' . éé cessaire puisqu il a t directement dans Meteor Donc si vous voyez une r éé f rence à la ligne de commande travers de ce livre ou sur Internet , mrt de Meteorite au vous pouvez simplement la remplacer par meteor . Ajouter un paquet Nous allons maintenant utiliser le syst è me de paquet de Meteor pour ajouter Bootstrap à . notre projet ' é Il n y a aucune diff é rence par rapport à la m ' -- ' thode usuelle c est à dire l inclusion manuelle des fichiers CSS et JavaScript de Bootstrap à part le fait que nous laissons le soin au mainteneur du paquet de le garder à jour ' Pendant qu on y est , . nous allons aussi installer Underscore Underscore est une biblioth è utilitaire pour JavaScript vraiment tr Le paquet . bootstrap que é . s pratique pour manipuler des donn ' est maintenu par l utilisateur , twbs:bootstrap . è es twbs , ce qui nous donne son nom complet En revanche , le paquet underscore , fournis par le framework ' fait partie des paquets c est pourquoi il ne poss è « ' officiel de pas d auteur » de Meteor qui sont : meteor add twbs:bootstrap meteor add underscore Notez que nous ajoutons Bootstrap 3. 'é Certaines captures d cran de ce livre ont avec une ancienne version de Microscope utilisant Bootstrap 2, éé ' t prises ce qui signifie qu elles seront l é è g rement diff é rentes de votre version . Commit 2-2 Avec les paquets Bootstrap et Underscore . ' Voir sur GitHub Lancer l instance ' è Vous devriez voir d de Bootstrap s maintenant quelques changements dans votre application suite à l ajout : Avec Bootstrap. Contrairement à la “ ” traditionnelle ' è mani , re d inclure des ressources externes ' lier aucun fichier CSS ou JavaScript car Meteor s en occupe pour nous ' ! . avantage des paquets Meteor parmi tant d autres ' nous n avons à ' ' Mais ce n est qu un À propos des paquets Lorsque nous parlons de paquets 'ê d é . tre pr cis Le c œ ( ) packages , dans le contexte de Meteor : Meteor distingue cinq types de paquets - ur de Meteor comprend lui m ). ê é me diff rents paquets essentiels Ils font partie de chaque application Meteor packages , ' Les paquets classique de Meteor se nomment isomorphes ). ( ' “ . ”, isopacks - accounts-ui maintenus par la team Meteor et sont directement ] // : . é é velopp ( ' . é ). ' appcache ou é é int gr s à é s sur le serveur de Vous pouvez les trouver sur Atmosphere ou avec la ( ) é sont des paquets sp local packages - ( , NPM packages cifiques que vous /packages . . Node js Packaged Modules ) sont des paquets , Bien qu ils ne fonctionneront pas tels quels avec Meteor tre exploit é sont sont simplement des isopacks er et placer dans le sous dossier Les paquets NPM ê ) Third party packages Les paquets locaux . . ou paquets packages meteor search . commande Node js - /# [ s par d autres utilisateurs qui les ont envoy paquet de Meteor pouvez cr . docs meteor com Les paquets tiers d core ce qui signifie qu ils peuvent fonctionner sur le client et sur le First party packages comme Meteor http ( et vous n aurez pratiquement jamais besoin de vous en soucier serveur il est bon s par les pr éé c ils peuvent . dents types de paquets La structure de fichiers d'une application Meteor , Avant de plonger dans le code ' l assemblage en les fichiers Puis cr ) build ez , ouvrez le dossier microscope.html , microscope.js ez quatre dossiers dans cr ' ne contienne que l essentiel é , é Enfin ( . il convient de configurer notre projet correctement main.html et main.js /client . Ne vous inquiétez pas si votre , . microscope.css . dans - et supprimez /microscope : /client , /server , /public , et /lib . application ne fonctionne plus pour le moment chapitres suivants et microscope Afin que nous allons remplir ces fichiers dans les - . Certains de ces sous dossiers sont particuliers conventions suivantes : n est ex Le code dans le dossier /client n est ex é galement tr é cut é . . que par le client . ( , , polices images ) etc vont dans le dossier /public . î /lib é sont charg sont charg é . s avant tous les autres . è s apr s les autres ' é dans l ordre alphab . tique Meteor ne vous contraint pas à utiliser de structure pr éé La structure sugg r - é e et d : é main.* Tout le reste est charg , é cut que par le serveur s utile de conna tre dans quel ordre Meteor charge vos fichiers Tous fichiers Au reste é é cut à la fois par le client et le serveur è Les fichiers dans . é /server Vos ressources statiques pas ' ' Le code dans le dossier Tout le reste est ex Il est Meteor traite les fichiers des dossiers selon les é é d finie , si vous ne le souhaitez é crite ci dessus correspond à notre façon de proc . , der ' elle n est en aucun cas inamovible Nous vous encourageons à consulter la documentation officielle de Meteor si vous souhaitez é davantage de d tails à ce sujet . Meteor est-il MVC ? é Si vous avez utilis ' , d autres frameworks demander si Meteor suit le pattern MVC , Pour faire court non . À la diff é d . finie à votre application , la façon qui fait sens pour nous Public ou pas ? ) Model View Controller rence de Rails é é fichiers pr ( , . tels que Ruby on Rails , vous pourriez vous ' Meteor n impose aucune structure de Dans ce livre , nous disposerons notre code de sans trop nous soucier des acronymes . è Pardonnez notre exc ' è . s de z le En pratique ' , car Microscope n utilisera pas de ressources statiques ' é signaler l utilit , de ce dossier /public , nous n aurons pas besoin du dossier ! é N anmoins , nous avons voulu vous car la plupart des applications Meteor comprendront tout au . moins quelques images , À ce propos ' ' vous avez peut ù C est l emplacement o -ê tre observ é é la pr é — ê ter attention à son contenu .meteor/packages et paquets intelligents ( é , Meteor enregistre son propre code peut conduire à un effet potentiellement d de pr ' sence d un dossier cach , sinon . sastreux nomm é .meteor . et y apporter des modifications ' é é Vous n aurez g , à de rares exceptions n ralement pas besoin aux fichiers .meteor/release , qui contiennent respectivement la liste de vos ) . et la version de Meteor en usage dans votre projet smart packages , Lorsque vous ajoutez des paquets et changez de version de Meteor changements en examinant ces deux fichiers é vous pouvez v rifier ces . Tirets_bas vs CasseAlternée ' ( ( ' é À l antique d caract è bat concernant l usage , re underscore CamelCase , , ' , ) — è apr ' é camelCase , , , s tout on é crit JavaScript è , et les classes CSS e , gle é ' - ê dans la syntaxe CSS elle m me ' parce que c est et non java qui utilisent des tirets ( . matiquement _ script ! qui utilisent des tirets bas è ( .ma-classe ). , font-family , text-align , en est que les tirets bas sont habituels dans les syst utilis de tirets bas é pourvu que vous l appliquiez syst Les noms de fichiers font exception à cette r ( mon_fichier.js ), ) ' nous utilisons la casse altern l usage avec JavaScript , maVariable , nous n ajouterons rien, sinon qu importe la convention que vous adopterez Dans cet ouvrage ' dans les noms d identifiants ma_variable , ou de la casse alternée comme dans comme dans , mes de fichiers La raison et que le tiret est ) . etc CSS sans coquetterie . Ce livre ne traite pas de CSS ééé nous avons pr ' , è n aurez d f r , s lors Ainsi , é pour ne pas vous ralentir avec des consid è vous mettre à disposition la feuille de style compl plus à vous en pr é occuper . è te d , rations de style é s le d . part Vous é Les feuilles de style CSS sont automatiquement charg ' de sorte qu elles doivent ê é tre plac . - /client/stylesheets/ , puis placez -, Allez y y un fichier cr é es à la vol é é - - , e par Meteor /client , plutôt que /public es dans le dossier le seraient les autres ressources statiques es et comprim comme ez y maintenant un sous dossier style.css avec le contenu suivant : .grid-block, .main, .post, .comments li, .comment-form { background: #fff; border-radius: 3px; padding: 10px; margin-bottom: 10px; -webkit-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); -moz-box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); box-shadow: 0 1px 1px rgba(0, 0, 0, 0.15); } body { background: #eee; color: #666666; } #main { position: relative; } .page { position: absolute; top: 0px; width: 100%; } .navbar { margin-bottom: 10px; } /* line 32, ../sass/style.scss */ .navbar .navbar-inner { border-radius: 0px 0px 3px 3px; } #spinner { height: 300px; } .post { /* For modern browsers */ /* For IE 6/7 (trigger hasLayout) */ *zoom: 1; position: relative; opacity: 1; } .post:before, .post:after { content: ""; display: table; } .post:after { clear: both; } .post.invisible { opacity: 0; } .post.instant { -webkit-transition: none; -moz-transition: none; -o-transition: none; transition: none; } .post.animate{ -webkit-transition: all 300ms 0ms; -moz-transition: all 300ms 0ms ease-in; -o-transition: all 300ms 0ms ease-in; transition: all 300ms 0ms ease-in; } .post .upvote { display: block; margin: 7px 12px 0 0; float: left; } .post .post-content { float: left; } .post .post-content h3 { margin: 0; line-height: 1.4; font-size: 18px; } .post .post-content h3 a { display: inline-block; margin-right: 5px; } .post .post-content h3 span { font-weight: normal; font-size: 14px; display: inline-block; color: #aaaaaa; } .post .post-content p { margin: 0; } .post .discuss { display: block; float: right; margin-top: 7px; } .comments { list-style-type: none; margin: 0; } .comments li h4 { font-size: 16px; margin: 0; } .comments li h4 .date { font-size: 12px; font-weight: normal; } .comments li h4 a { font-size: 12px; } .comments li p:last-child { margin-bottom: 0; } .dropdown-menu span { display: block; padding: 3px 20px; clear: both; line-height: 20px; color: #bbb; white-space: nowrap; } .load-more { display: block; border-radius: 3px; background: rgba(0, 0, 0, 0.05); text-align: center; height: 60px; line-height: 60px; margin-bottom: 10px; } .load-more:hover { text-decoration: none; background: rgba(0, 0, 0, 0.1); } .posts .spinner-container{ position: relative; height: 100px; } .jumbotron{ text-align: center; } .jumbotron h2{ font-size: 60px; font-weight: 100; } @-webkit-keyframes fadeOut { 0% {opacity: 0;} 10% {opacity: 1;} 90% {opacity: 1;} 100% {opacity: 0;} } @keyframes fadeOut { 0% {opacity: 0;} 10% {opacity: 1;} 90% {opacity: 1;} 100% {opacity: 0;} } .errors{ position: fixed; z-index: 10000; padding: 10px; top: 0px; left: 0px; right: 0px; bottom: 0px; pointer-events: none; } .alert { animation: fadeOut 2700ms ease-in 0s 1 forwards; } -webkit-animation: fadeOut 2700ms ease-in 0s 1 forwards; -moz-animation: fadeOut 2700ms ease-in 0s 1 forwards; width: 250px; float: right; clear: both; margin-bottom: 5px; pointer-events: auto; / / . client stylesheets style css Commit 2-3 Structure de fichiers r Voir sur GitHub é é . organis e ' Lancer l instance Au sujet de CoffeeScript , Dans ce livre nous é , utiliser CoffeeScript CoffeeScript et vous . crirons les scripts en JavaScript pur ' Meteor n est pas en reste ê é: tes par meteor add coffeescript . éé Si vous pr f é rez n anmoins Ajoutez simplement le paquet Déploiement 2.5 SIDEBAR ' ' , Certaines personnes aiment travailler tranquillement sur un projet jusqu à ce qu il soit parfait ' ô tandis que d autres ne peuvent pas attendre de le montrer au monde le plus t Si vous ' ê l instant , vous ê tes libre de sauter ce chapitre de temps pour apprendre comment d ' qu il vous faut é . ' ' é ô é rez plut ô é, D un autre c t t d velopper en local pour si vous voulez prendre un peu ployer votre application Meteor en ligne é , nous avons ce ' . ployer une application Meteor de plusieurs façons ' tes libres d utiliser chacune d elles à n importe quelle d f . Nous allons apprendre comment d ê éé tes le premier type de personne et que vous pr . t possible é Vous tape de votre processus de veloppement si vous travaillez sur Microscope ou une autre application Meteor . -! Allons y Présentation des Sidebars Ceci est un chapitre sidebar ( apart , sur des sujets concernant Meteor Donc si vous pr éé è f sauter sans probl rez plut ) é, les Sidebars donnent une vision approfondie é ind . pendamment du reste du livre ô t continuer à travailler sur Microscope , vous pouvez le . me maintenant et y revenir plus tard Déployer sur Meteor é D - ployer sur un sous domaine Meteor simple et la premi è i e . re que nous testerons , dans ses premiers jours D ( . . http://myapp.meteor.com ) Ça peut ê é tre utile pour pr ' est l option la plus senter votre application . ou de mettre en place rapidement un serveur de test é . ployer sur Meteor est simple application Meteor et tapez : Ouvrez juste votre terminal allez dans le r é pertoire de votre meteor deploy myapp.meteor.com û, Bien s éé pr f r vous devrez faire attention à remplacer ' é rence un qui n est pas d ' é. . Meteor ' è ' éé f é il vous sera demand de cr ' é de er un compte é s quelques secondes vous serez capable d acc der à votre ' rer à la documentation officielle pour plus d informations sur des é choses comme acc , avec un nom de votre choix http://myapp.meteor.com . application à l adresse Vous pouvez vous r , ploiement d application Et si tout va bien apr ” myapp jà utilis é Si c est votre premier d “ é der directement à la base de donn é configurer un domaine personnalis é es de votre instance h é berg e ou . pour votre application Déployer sur Modulus é Modulus est une bonne option pour d fournisseurs PaaS ( - -- ) platform as a service é quelques personnes qui ex . . ployer des applications Node js ' C est un des qui supporte officiellement Meteor , é et il y a d jà . cutent des applications Meteor en production dessus é Vous pouvez en apprendre plus à propos de Modulus en lisant leur guide de d ploiement . pour application Meteor Meteor Up , Bien que de nouvelles solutions de cloud apparaissent chaque jour è avec leurs lots de probl mes et de limitations . ’ , é Donc aujourd hui d elles viennent toujours ployer sur votre propre . serveur reste le meilleur moyen de mettre votre application Meteor en production é chose est que d - é d ' ê ployer vous m me n est pas si simple ploiement en production de qualit Meteor Up ( ou mup surtout si vous recherchez un é. de son petit nom ' , ) è est une autre tentative de solution à ce probl é un outil en ligne de commande qui s occupe de la configuration et du d é Regardons comment d La seule . ployer Microscope en utilisant Meteor Up me , avec . ploiement pour vous , Avant tout autre chose $ é qui d 5 marre à , par mois è rapidement des probl Up devrait ê ' nous aurons besoin d un serveur , ou AWS é, ). , un login ( habituellement ô nous en aurons bient t besoin ! root ( vous aurez mais si vous cherchez juste un terrain de jeu Meteor Quel que soit le service que vous choisissez serveur , Nous recommandons Digital Ocean qui fournit des Micro instances gratuitement mes de scalabilit tre suffisant . , ou vous obtiendrez trois choses ) : ' l adresse IP de votre - ubuntu , et un mot de passe. Gardez ô é, les de c t Initialiser Meteor Up é Pour d , marrer ' nous aurons besoin d installer Meteor Up via npm comme suit : npm install -g mup é Nous allons cr er ensuite un r Meteor Up pour un d raisons : ' é é é pertoire s par ploiement particulier c est mieux d 'é ' . é, é sp cial qui contiendra vos configurations é Nous utilisons un r é viter d inclure des donn es priv é é pertoire s par é ô es dans votre d p é pour deux , t Git é sp cialement . si vous travaillez sur du code public è Deuxi , mement é en utilisant plusieurs r pertoires s è . plusieurs configurations Meteor Up en parall le é é, par s Ça sera tr é nous serons capables de g è rer é s pratique pour d ployer des . instances de production et de tests par exemple é Cr ons ce nouveau r é pertoire et utilisons le pour initialiser un nouveau projet Meteor Up mkdir ~/microscope-deploy cd ~/microscope-deploy mup init : Partage avec Dropbox ' Un bon moyen de s assurer que vous et votre é de d é . ploiement est simplement de cr votre Dropbox , é ê quipe utilise les m mes configurations er un dossier de configuration Meteor Up dans ou un service similaire Configuration Meteor Up Quand on initialise un nouveau projet , Meteor Up va cr er deux fichiers pour vous : mup.json settings.json . et mup.json é gardera toutes vos configurations relatives au d settings.json , jetons analytics 'é L é é éé faut g n r , ploiement ' pendant que contiendra toutes les configurations relatives à l application ) .. etc tape suivante est de configurer votre fichier d é par ( , jetons OAuth mup.json . Voici le fichier mup.json par mup init , et tout ce que vous avez à faire est de remplir les blancs : { //info d'authentication serveur "servers": [{ "host": "hostname", "username": "root", "password": "password" //ou fichier pem (authentification basée sur ssh) //"pem": "~/.ssh/id_rsa" }], //installer MongoDB sur le serveur "setupMongo": true, //chemin de l'application (répertoire local) "app": "/path/to/the/app", } //configuration environnementale "env": { "ROOT_URL": "http://supersite.com" } . mup json . Traversons chacune de ces configurations Server Authentication ' é Vous noterez que Meteor Up supporte l authentification par cl mots de passe ( ), PEM Note importante ' : donc il peut ê é tre utilis ' s priv é é es et bas es sur des . avec la plupart des fournisseur de cloud ' Si vous choisissez d utiliser l authentification par mot de passe é sshpass vous d avoir install é au pr alable (éé r f ). , - assurez rez vous à ce guide Configuration MongoDB L 'é é tape suivante est de configurer une base de donn ' ' . es MongoDB pour votre application Nous vous recommandons d utiliser Compose ou n importe quel autre fournisseur de cloud . MongoDB car ils offrent un support professionnel et de meilleurs outils de gestion é Si vous avez d ' ' é cid , d utiliser Compose MONGO_URL d environnement , MongoDB avec Meteor Up dans le bloc mettez juste setupMongo mettez env à false et ajoutez la variable ' mup.Json . Si vous décidez d héberger de setupMongo true à ' et Meteor Up s occupera du . reste ' Chemin de l application Meteor é Maintenant que notre configuration Meteor Up a pris vie dans un r é pertoire diff , rent nous éé allons avoir besoin de faire pointer Meteor Up vers notre application en utilisant la propri t app . Entrez juste votre chemin local complet, que vous pouvez trouver en utilisant la pwd commande dans le terminal quand vous ê é tes plac dans votre r é pertoire . ' Variables d environnement é Vous pouvez sp ' cifier toutes les variables d environnement de votre application ) ' ROOT_URL , MAIL_URL , MONGO_URL , etc. é à l int rieur du bloc env . ( telles que Configurer et déployer ' é Avant qu on puisse d é h ' ployer nous allons avoir besoin de configurer le serveur pour qu il puisse berger des applications Meteor dans une seule commande ! . La magie de Meteor Up encapsule ce processus complexe mup setup Ça va prendre quelques minutes en fonction de la performance du serveur et de la é é connectivit r . seau application avec : Apr è è s le succ ' , s de l installation é vous pouvez finalement d ployer votre mup deploy Ça va compacter votre application Meteor , é et la d ployer sur le serveur que nous venons de . mettre en place Afficher les journaux Les journaux sont importants et Meteor Up fournit un moyen tr é mulant la commande è s facile de les manipuler en tail -f . Tapez juste : mup logs -f ' é Ça vous donne une vue d ensemble des capacit é rons de visiter le d nous vous sugg é Ces trois façons de d . des cas û, Bien s r . s de Meteor Up é ô p … -ê ou peut . ployer des applications Meteor devrait ' nous savons que plusieurs d entre vous pr tre un autre livre ! , t Github de Meteor Up . complet et configurer leur serveur Meteor de bout en bout jour ' Pour plus d informations ê tre suffisantes pour la plupart éé f ô reront avoir un contr ' le Mais c est un sujet pour un autre Templates 3 é Pour faciliter le d ' é vers l int . rieur , veloppement dans Meteor ' , En d autres termes é nous la rattacherons à la m ' nous allons adopter une approche de l ext nous allons cr ' é / , er une simple page html javascript . é rieur puis canique interne de l application plus tard ' Ce qui veut dire que dans ce chapitre nous nous occuperons de ce qu il se passe dans le é r pertoire /client . ' é Si vous ne l avez pas d é r pertoire /client jà fait é et ins , é cr ez un nouveau fichier appel rez le code suivant é main.html dans votre : <head> <title>Microscope</title> </head> <body> <div class="container"> <header class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <a class="navbar-brand" href="/">Microscope</a> </div> </header> <div id="main"> {{> postsList}} </div> </div> </body> / . client main html . Ce sera le template principal de notre application HTML except ' é ' pour la balise d inclusion de template d insertion pour le template é cr é er deux templates suppl Les templates Meteor postsList . mentaires ' Comme vous pouvez le voir c est du langage {{> postsList}} , qui est un point ô. que nous verrons bient t Nous allons maintenant En son c ' œ , ur é un site d actualit é s social est compos ' é d articles organis , s en listes ' et c est . exactement de cette façon que nous allons organiser nos templates é Cr é ons un r , templates /templates pertoire dans ' /client . C est ici que nous mettrons tous nos et pour garder les choses en ordre nous allons /templates é é galement cr juste pour nos templates relatifs aux articles ( ). er /posts dans posts Recherche de fichiers é Meteor est g . nial pour trouver les fichiers é dans le r pertoire ' Ça signifie ê m me r ( ' 'é include ) crire manuellement des chemins . pour les fichiers javascript ou CSS é galement que vous pourriez aussi bien mettre tous vos fichiers dans le é , pertoire ou m ê ê me tout votre code dans le m é Meteor compilera tout dans un seul fichier minifi ô plut vous mettez votre code /client , Meteor le trouvera et le compilera proprement. Ce qui signifie que vous n avez jamais besoin d d inclusion ù Peu importe o é t les choses bien organis me fichier . , de toute façon Mais sachant que nous garderons es et utiliserons une structure de fichier la plus claire . possible ê Nous sommes finalement pr é t pour cr er notre second template client/templates/posts , créez posts_list.html : <template name="postsList"> <div class="posts"> {{#each posts}} {{> postItem}} {{/each}} </div> </template> / / / _ client templates posts posts Et post_item.html : . list html . Dans <template name="postItem"> <div class="post"> <div class="post-content"> <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3> </div> </div> </template> / / / _ ' name="postsList" client templates posts post Notez l attribut . item html de la balise template ù Meteor pour garder la trace de quel template va o é Il est temps de pr html simple , è senter le syst ( . ' C est le nom qui sera utilis : inclusions . par ). le nom du fichier n est pas important me de templating de Meteor avec trois choses en plus ' é ( é parfois d , . Spacebars é sign Spacebars est du e sous le nom de “ ”), partials expressions et block helpers Les Inclusions utilisent la syntaxe {{> templateName}} , et indiquent simplement à Meteor de ê remplacer le champ par le template du m Les Expressions telles que {{title}} la valeur du template helper comme d ' ). me nom ( dans notre cas éé appellent une propri t ) postItem . ' , de l objet courant é fini dans le template manager courant ( ou retourne plus d information sur ce sujet plus tard , Finalement é les block helpers sont des balises sp {{#each}}...{{/each}} que ou ô ciales qui contr , lent le flux du template {{#if}}...{{/if}} . Aller plus loin Vous pouvez consulter la documentation Spacebars si vous voulez en apprendre . plus à propos de Spacebars é Arm de cette connaissance , é vous pouvez dor . navant comprendre ce qui se passe ici tels è Premi , rement dans le template {{# avec le block helper template 'ù D o ' each postItem . posts vient cet objet postsList , nous faisons des itérations sur un objet posts }}…{{/ ? }}. each , Ensuite . Bonne question é pour chaque it ' C est un template helper {{title}} - postItem lui m ê me est assez simple retournent des propri éé t ; l imaginer comme un substitut à un contenu dynamique Le template ration nous allons inclure le . vous pouvez vous . Il utilise trois expression : {{url}} , s du document et {{domain}} et appelle un template . helper Template Managers ' Jusqu à maintenant é balises parsem , é nous avons travaill . es dedans pages HTML classiques é logique s ' é, par , Afin d exister s ), - ê qui contiennent du javascript . un template a besoin de helpers é ) ' les templates qui est du HTML avec quelques Contrairement aux autres langages tel que PHP dients crus ( é vos donn ' , dans les variables , pendant que le r ' . ê me des . Vous pouvez imaginer ces helpers comme un es ) é et les pr ô pare é le du template est limit c est celui des helpers qui fait en r valeur à chaque variable ou m me , avant de dresser une assiette qu il va donner à un serveur qui va ensuite vous les pr En d autres termes ( Meteor garde les templates et leur et ces templates ne font rien par eux m chef qui prend des ingr ( , , avec Spacebars é é alit ' é senter . é à l affichage et aux it rations le gros du travail en assignant une Des contrôleurs ? ' Il peut sembler tentant de penser aux fichiers contenant tous les helpers d un template comme une sorte de puisque les é è l g “ é rement diff leurs rent “ . é Nous avons donc d simplement à ”( ô contr é cid “ les helpers du template , Mais cela peut ) ê é ” ou à “ ont habituellement un r ” la logique du template . , ô tre ambigu éé f le rer lorsque nous au template nous allons nommer les fichiers contenant les helpers comme mais avec une extension client/templates/posts ). controller de ne pas utiliser cette terminologie et de nous r Pour garder les choses simples , leur du moins du point de vue MVC parlons du code JavaScript associ le template ”( ô contr . . é js Cr ons donc directement posts_list.js et commençons à construire notre premier helper dans : var postsData = [ { title: 'Introducing Telescope', url: 'http://sachagreif.com/introducing-telescope/' }, { title: 'Meteor', url: 'http://meteor.com' }, { title: 'The Meteor Book', url: 'http://themeteorbook.com' } ]; Template.postsList.helpers({ posts: postsData }); / / / _ client templates posts posts ' . list js Si vous l avez fait correctement navigateur : , vous devriez avoir quelque choses de similaire à ça dans votre Nos premiers templates avec données statiques . Nous faisons deux choses ici tableau è Premi , rement ' é utilisant des donn è , mement es prototype dans le . nous utilisons la fonction é posts , Si vous vous souvenez ) prochain chapitre nous allons tricher en / Template.myTemplate.helpers() qui retourne le tableau nous utilisons ce helper de <template name="postsList"> <div class="posts page"> {{#each posts}} {{> postItem}} {{/each}} </div> </template> / ( es statiques template helper appel / é rons des donn postsData . Ces données viendraient normalement de la base de données, mais comme nous n avons pas encore vu comment faire Deuxi é nous ins _ client templates posts posts . list html postsData posts é d é pour d finir un . fini auparavant dans notre template postsList : é D finir le helper de ' posts , implique qu il est maintenant disponible pour notre template é donc notre template sera capable de faire des it ' d envoyer chaque objet vers le template rations sur le tableau postsData , et postItem . Commit 3-1 é Avec le template basique de posts list et les donn es sta … ' Voir sur GitHub Lancer l instance Le helper domain ê De m me , é nous allons cr er post_item.js pour y renseigner la logique du template de postItem : Template.postItem.helpers({ domain: function() { var a = document.createElement('a'); a.href = this.url; return a.hostname; } }); / / / _ client templates posts post Cette fois la valeur du helper è mod . item js domain le est bien plus commun . simples ( ' n est pas un tableau ) et plus utile é compar , mais une fonction anonyme éé à nos pr c . Ce é dents exemples de donn es Afficher les domaines pour chaque lien. Le helper ù Mais o domain . - ' prend une URL et retourne son domaine via un peu de magie JavaScript prend il l url dans un premier temps ? é Pour r pondre à cette question nous avons besoin de revenir à notre template posts_list.html . Le block helper {{#each}} , tableau è é il ins re ' ' galement les valeurs dans é Ça signifie qu à l int this rieur des deux balises , successivement 'é et ça s , sait ce que veut dire ' é à l int ' é é. rieur du bloc de l objet it {{#each}} , chaque article this.url {{title}} et {{url}} this.title et this.url si nous utilisons this rations sur notre r ( ) é ( post_item.js ). post est assign à tend dans le manager du template inclus Nous comprenons maintenant pourquoi plus é ne fais pas seulement des it ' ' . retourne l URL de l article courant dans notre template De post_item.html , Meteor . et retourne les valeurs correctes Commit 3-2 Configurer un helper ` ` domain à partir du ` `. postItem ' Voir sur GitHub Lancer l instance Magie JavaScript é Bien que ce ne soit pas sp ”. JavaScript Premi è , rement é stockons en m cifique à Meteor . , comme nous venons de le voir ) , é a pour r é fonctionnalit ! pour ê tre a avec ancre vide et la ' é t é sp ciale é cup ' gal à l URL de l article courant éé hostname ' . de l 'é é l ment rer le nom de domaine du lien sans le reste de l URL C est juste une liste de donn suivant ' href nous prenons avantage de la propri Si vous avez suivi correctement ' (‘ ’) Magie l objet en cours de traitement se trouve dans un this . Finalement ons une balise “ moire ' helper voici une petite explication de é nous cr Nous modifions ensuite l attribut ( , é s temps r , ' . vous devriez voir une liste d articles dans votre navigateur é es statiques . el de Meteor , donc ça ne tient pas compte des avantages des Nous verrons comment changer ça dans le chapitre Hot Code Reload ' ê Vous avez pu noter que vous n aviez m ê la fen ' me pas eu besoin de recharger manuellement é tre de notre navigateur quand vous avez modifi é C est parce que Meteor traque tous les fichiers dans le r î é rafra chit automatiquement le navigateur quand il d ' . vos fichiers , pertoire de votre projet et ' tecte une modification sur l un . d entre eux Le Hot Code Reload de Meteor est tr î è , s intelligent application entre deux rafra chissements ! é en pr servant m ê 'é me l tat de votre Utiliser Git & GitHub 3.5 SIDEBAR - é ô GitHub est un d version Git . projets , p é t social pour les projets open source bas è et sa fonction premi ' è sur le syst me de contr ô le de re est de partager du code facilement et de collaborer sur des ' . Mais c est aussi un merveilleux outil d apprentissage Dans cette sidebar , nous allons rapidement discuter de quelques moyens pour que vous puissiez utiliser GitHub afin de suivre . avec Discover Meteor é Cet apart suppose que vous n , avec les deux vous ê 'ê . tes pas familier avec Git et GitHub tes libres de passer au chapitre suivant ! Si vous ê é tes d ' jà à l aise Être committed ' é ô Le bloc de travail de base d un d comme une image de l 'é p . t git est le commit Vous pouvez imaginer le commit é . tat de votre code à un moment donn dans le temps Au lieu de vous donner simplement le code fini pour Microscope chaque é é tape du d , Par exemple ' veloppement , , nous avons pris ces images à . et vous pouvez toutes les voir en ligne sur GitHub éé c est ce à quoi le dernier commit du chapitre pr c dent ressemble : Un commit Git vu sur GitHub. Ce que vous voyez ici est le “ ”( diff pour “ difference . termes les changements introduits par ce commit post_item.js à partir de rien , ”) Dans ce cas , nous avons cr é et donc tout son contenu est surlign Comparons avec un exemple plus loin dans le livre ' post_item.js , en d autres du fichier éé le fichier . en vert : Modifier du code. , Cette fois é seuls les fichiers modifi , Et bien sur é parfois nous n avons pas ajout é : supprim ' é s sont surlign es . s en vert é ou modifi , de lignes de code mais nous en avons Supprimer du code. , Bien è nous venons de voir la premi re utilisation de GitHub : ' avoir une vue d ensemble de ce é. qui a chang Explorer le code d'un Commit ' , La vue d un commit de Git nous montre les changements inclus dans ce commit ' é, vous voudriez voir les fichiers qui n ont pas chang doit ressembler à une , Une fois encore , commit é s ' mais parfois juste pour s assurer de ce à quoi leur code . tape du processus . GitHub a quelque chose à nous proposer cliquez sur le bouton Browse code : Quand vous ê tes sur une page de Le bouton Browse code. è Vous allez maintenant avoir acc ' é ô s au d p é t tel qu il est à ce moment sp cifique du commit Le dépôt au commit 3-2. ' GitHub ne nous donne pas d indices visuels que vous mais vous pouvez comparer avec la vue master “ ê , tes en train de regarder un commit ” normale ' ' et voir d un coup d oeil que la é structure de fichiers est diff rente : Le dépôt au commit 14-2. Accéder à un commit localement ' . Nous venons de voir comment explorer le code entier d un commit en ligne sur GitHub ê comment faire si vous voulez faire la m me chose localement é vouloir ex ? é cuter une application localement à un commit sp , Par exemple Mais vous pourriez cifique pour voir comment elle se . comportait à ce moment du processus Pour faire ça il y a plusieurs commande ' é tapes ( , bien ' git . Pour les débutants, assurez vous d ) , éé d autres termes t l ) dans ce livre du moins charger une copie localement ' avec l utilitaire en ligne de é. avoir Git install é ô le d p Ensuite clone t de Microscope avec : ( en git clone https://github.com/DiscoverMeteor/Microscope.git github_microscop e Ce é github_microscope ' à la fin est simplement le nom du r . allez cloner l application ù Dans le cas o é vous avez d pertoire local dans lequel vous é jà un r pertoire microscope , prenez é juste un nom diff rent ( ' ' é Allez dans le r ê il n y a pas besoin d avoir le m pertoire avec la commande cd p t GitHub ' afin de pouvoir utiliser l utilitaire en ligne de git : commande ). é ô me nom que le d cd github_microscope Quand nous avons clon ' , é é ô le d , éé t de GitHub nous avons t l é charg tout le code de . l application ce qui veut dire que nous avons le code du dernier commit , Heureusement il y a un moyen de revenir dans le temps et de é sp p . cifique sans affecter les autres Essayons : “ ” consulter un commit git checkout chapter3-1 Note: checking out 'chapter3-1'. You are in 'detached HEAD' state. You can look around, make experimental changes and commit them, and you can discard any commits you make in this state without impacting any branches by performing another checkout. If you want to create a new branch to retain commits you create, you may do so (now or later) by using -b with the checkout command again. Example: git checkout -b new_branch_name HEAD is now at a004b56... Added basic posts list template and static data. Git vous informe que vous Git a éé t é, utilis ê tes dans un é tat “ ”, detached HEAD vous pouvez observer les commits pass é ' ce qui signifie qu aussi loin que . s mais vous ne pouvez les modifier Vous pouvez imaginer ça comme un sorcier en train de consulter le pass é dans sa boule de . cristal ( Notez que Git a é galement des commandes qui vous laisse modifier des commits pass é serait plus comme un voyageur du temps qui revient dans le pass é craser un papillon , ' mais c est hors du champ de cette br è é. s Ça et peut potentiellement ) . ve introduction chapter3-1 La raison pour laquelle vous avez pu simplement taper é tagg tous les commits de Microscope avec le marqueur de chapitre correcte , le cas , vous auriez besoin de trouver le hash du commit Une fois encore , GitHub nous simplifie la vie ' coin en bas à droite du champ d ent ê . - est que nous avons pre . 'é Si ça n ou un identifiant unique . tait pas Vous pouvez trouver un hash de commit dans le , te de commit bleu - comme ci dessous : Trouver un hash de commit. ' Testons avec un hash au lieu d un tag : git checkout c7af59e425cd4e17c20cf99e51c8cd78f82c9932 Previous HEAD position was a004b56... Added basic posts list template and st atic data. HEAD is now at c7af59e... Augmented the postsList route to take a limit , Et finalement ê si nous voulons arr é voulons revenir dans le pr git checkout master sent ? ter de regarder dans notre boule de cristal et que nous Nous demandons à Git de consulter la branche master : ' Notez que vous pouvez lancer l application avec la commande lorsque vous ê 'é “ tes dans l meteor update tat ”. detached HEAD pas inclus dans le repo Git de Microscope -ê Vous aurez peut , si Meteor se plaint de paquets manquant , ê meteor à tout moment ' m me tre besoin de lancer d abord ' puisque le code des packages n est . Perspective historique Voici un autre sc ' é nario commun vous n aviez pas vu avant é. chang . : vous regardez un fichier et remarquez des changements que , Le fait est que vous ne vous souvenez plus quand le fichier a ’ é è “ t , Vous pourriez juste regarder les commits un par un jusqu à que vous trouviez le bon , rement ": History é acc ' é ô dez à l un des fichiers de votre d p . d historique de GitHub mais il y a un moyen plus facile grâce à la fonctionnalit Premi ' éé , t sur GitHub puis localisez le bouton Bouton History de GitHub. é Vous avez maintenant une liste claire de tous les commits qui ont affect particulier : s ce fichier en Afficher l'historique d'un fichier. Le jeu du blâme é Pour d , velopper è : regardons Blame de plus pr Bouton Blame de GitHub. s é Cette vue claire nous montre ligne par ligne qui a modifi ' , d autres termes le fichier , et dans quel commit ): qui est à blâmer quand les choses ne fonctionnent plus ( en Vue Blame de GitHub. Git est un outil assez complexe . tout dans un seul chapitre . - et GitHub aussi , En fait Mais heureusement . mesure du livre é donc nous ne pouvons pas esp é nous avons à peine effleur , ê avec ces outils -, m me ce petit é rer couvrir la surface de ce qui est possible é chantillon prouvera son utilit au fur et à Collections 4 Dans le chapitre un , nous avons parl automatique des donn é ' é d une fonctionnalit é é es sp é é ciale qui prend soin de conserver vos donn , ôé e permanente MongoDB é c t , serveur et qui ensuite la synchronise en é. é allons commencer par cr Les collections sont un ' é é Posts er une collection appel e l é posts.js jà fait , é cr . dedans , é collections/ pertoire Finalement ajoutez dans : . lib collections posts js ' Commit 4-1 ' Ajout d une collection d articles Voir sur GitHub ' . ' pour s assurer lib , donc, si vous ne lib , et ensuite un fichier Posts = new Mongo.Collection('posts'); / , ainsi finies en premier on les placera dans le dossier ez un r donc nous pour les stocker dedans ment central quelle que soit l application é l avez pas d , s entre les utilisateurs ' éé qu elles sont toujours d / es el avec chaque navigateur connect Nous voulons que nos articles soient permanents et partag ' la synchronisation . é temps r , es entre le client et le serveur Une collection est une structure de donn dans la base de donn coeur de Meteor Lancer l instance Var ou pas Var ? Dans Meteor , - le mot cl é var voulons rendre la collection ’ ' . limite le champ d un objet au fichier courant Posts ' è , disponible dans l application enti - raison pour laquelle nous n utilisons pas le mot cl é re é le diff rent . é : moire du navigateur ' . , : ' ce qui signifie qu elle ne sont pas permanentes , Le stockage du navigateur é les choses comme les variables JavaScript sont stock sont locales à l onglet courant du navigateur fermez î elles t que vous le é les navigateurs peuvent aussi stocker des donn é es perdureront session apr ( es ô et dispara tront aussit . re permanente en utilisant des cookies ou le stockage local donn : è mani chacune : moire du navigateur dans la m es é La m ' et c est la é , ô nous var ici Les applications web disposent de trois façons basiques de stocker des donn correspondant à un r , Ici è , s session mais disponibles dans tous les onglets . ) ' es de Bien que ces elles sont locales à l utilisateur courant et ne peuvent pas ê é tre partag es facilement entre les utilisateurs La base de donn ' é ôé es c t serveur : é le meilleur endroit pour stocker des donn ' es que l on veut aussi rendre accessibles à plus d un utilisateur est la bonne vieille base de é donn e ( MongoDB é é tant la solution par d , Meteor utilise les trois façons ( comme nous le verrons bient é : donn es faut pour les applications Meteor é et synchronisera parfois les donn ) ô . t , Cela dit é la base de donn ' es d un endroit à l autre e reste la source é . elle contient la copie originale de vos donn ). ' “ ” canonique de es Client & Serveur é Le code contenu dans des dossiers diff . deux contextes , Cependant Ainsi ' , la collection rents de Posts client/ /serveur ou é sera lanc dans les . est disponible à la fois sur le client et sur le serveur ce qu elle fait dans chaque environnement peut ê é tre bien diff . rent , Sur le serveur , MongoDB é la collection à pour objectif de communiquer avec la base de donn et de lire et é . crire chaque changement , Dans cette optique ê elle peut es é tre compar é . à une librairie standard de base de donn , En revanche sur le client canonique collection , ensemble ( . , e ' - é la collection est une copie d un sous ensemble de la r ôé La collection c ) la plupart du temps t elle et client est constamment mise à jour avec ce sous è de mani re transparente . - e Console vs Console vs Console , Dans ce chapitre , nous allons commencer à utiliser la console du navigateur , faut pas confondre avec le terminal ' rapide de chacuns d entre eux . le shell Meteor ou le shell Mongo . Terminal Le Terminal é Appel ôé C t serveur ' è par votre syst . me d exploitation console.log() . affiche la sortie ici é: $ . Invit Egalement connu comme : Console Navigateur , Shell . Bash ' qu il ne Voici un topo La console navigateur é Appel ôé C t client é: Invit , depuis le navigateur ❯ console.log() é ex . cute du code JavaScript . affiche la sortie ici . Egalement connu comme : , Console JavaScript . Console Devtools Shell Meteor Le Shell Meteor Le Shell Meteor é Appel depuis le terminal avec è Il vous donne un acc é: > . Invit Shell Mongo meteor shell . ôé s direct au code c t . serveur de votre application Le Shell Mongo é Appel meteor mongo . depuis le terminal avec ' è Vous donne l acc ' é s direct à la base de donn . e de l application é: > . Invit Egalement connu comme : , Notez que dans chaque cas ($, ❯, or ) > 'ê vous n éé c éé tes pas suppos . comme partie de la commande ' é qui ne commencent pas avec l invit pr . Console Mongo . è crire le caract ' é re de l invit Et vous pouvez deviner que les lignes sont la sortie (é r ) sultat de la commande dente Collections côté Serveur Revenons sur le serveur ; ôé Dans votre code c t Posts.insert() ou posts , serveur 'é ça vous permet d . é ' , e Mongo é toujours en cours d ex . crire des commandes Mongo telles que es dans Mongo Pour regarder dans la base de donn application . e Mongo Posts.update() , et et elles feront les changements dans la collection é stock é la collection agit comme une API dans votre base de donn , Ensuite ouvrez un second terminal ), cution sur votre premier é ex cutez la commande meteor mongo lequel vous pouvez taper des commandes Mongo standards pouvez quitter avec le raccourci clavier ) é et allez dans le r ( meteor est pertoire de votre , pour initier un shell Mongo ( , et comme habituellement dans vous ctrl+c . Par exemple, insérons un nouvel article : meteor mongo > db.posts.insert({title: "A new post"}); > db.posts.find(); { "_id": ObjectId(".."), "title" : "A new post"}; Le Shell Mongo Mongo sur Meteor.com ' é Notez qu en h acc é bergeant votre application sur *. é der au shell Mongo de votre application h Et pendant que vous y ê tes , votre application en tapant La syntaxe de Mongo est famili vous pouvez é . Meteor com , e avec galement retrouver les journaux meteor logs myApp . è , re pas plus de manipulation de donn ' . vu qu elle utilise une interface JavaScript é é , es dans le shell Mongo ' temps en temps pour vous assurer de ce qui s y trouve galement meteor mongo myApp . é berg vous pouvez ( ) logs de Nous ne ferons mais vous pourrez y jeter un oeil de . Collections côté Client Les collections sont plus int é ôé ressantes c . t client , Mongo.Collection('posts'); sur le client ôé t client en tant que é , donn es “ é ”, cache è et offre un acc ê ce que vous local dans le navigateur de la collection Mongo r c é Quand vous d elle . ' clarez Posts = new é tes en train de cr er est un cache ' Quand nous parlons d une collection - nous voulons dire qu elle contient le sous ensemble de vos é . s rapide à ces donn es Il est important de comprendre ces points qui sont fondamentaux dans la façon dont Meteor fonctionne . é é , En g n é documents stock ral ôé une collection c t s dans la collection Mongo envoyer toute la base de donn é - client consiste en un sous ensemble de tous vos ). es au client ( apr è , s tout é é , en g n ral nous ne voulons pas è Deuxi ' , mement qu y acc é ces documents sont stock é é. der est tout simplement instantan ou la base de donn , client é é es pour r vu que les donn cup é é es sont d é é - é ' moire du navigateur ce qui signifie Donc il n y a pas de longs trajets vers le serveur rer les donn jà pr , é s dans la m es quand vous appelez Posts.find() sur le é . charg es Présentation de MiniMongo ' é L impl ôé mentation de Mongo c é une impl t , mentation parfaite ' . client s appelle MiniMongo ' ' C est n est pas encore . occasionel de Mongo qui ne fonctionneront pas dans MiniMongo les fonctionnalit é et vous pourrez rencontrer quelques fonctionnalit , Cependant s toutes é s qui sont dans ce livre fonctionne de façon similaire dans Mongo et . MiniMongo Communication Client-Serveur é La cl û de vo la collection c Plut ôé t serveur du m ' ô t que d expliquer ça en d Commençons par ouvrir ' . l une des deux , À ce moment contextes éé pr c ( ôé te de tout ça est comment la collection c , Ensuite 2 fen ê me nom é , tail ê ( 'posts' t ). ' , é et acc . der à la console du navigateur dans ouvrez le shell Mongo dans le terminal vous devriez pouvoir trouver le document cr ' es avec dans notre cas regardons juste ce qu il se passe tres de navigateur é client synchonise ses donn . éé é é pr c demment dans les trois notez que l interface utilisateur de notre application affiche toujours nos trois dents posts factices . - > db.posts.find(); {title: "A new post", _id: ObjectId("..")}; Le Shell Mongo ). Ignorez les pour le moment Posts.findOne(); {title: "A new post", _id: LocalCollection._ObjectID}; ❯ Console du premier navigateur é Cr ' ons un nouvel article d insertion . ' , Dans l une des consoles de navigateur : é ex cutez une commande ❯ 1 Posts.find().count(); Posts.insert({title: "A second post"}); 'xxx' ❯ Posts.find().count(); 2 ❯ Console du premier navigateur ' , Sans surprise l article a é é éé t cr . dans la collection local Maintenant v é rifions Mongo : db.posts.find(); {title: "A new post", _id: ObjectId("..")}; {title: "A second post", _id: 'yyy'}; ❯ Le Shell Mongo Comme vous pouvez le voir 'é besoin d avons tout ! é , ' l article a é é éé t ' cr crire une seule ligne de code d envoi au serveur crit une seule ligne de code é dans la base de donn ( , bien , es Mongo ) : new Mongo.Collection('posts') . ê Ouvrez la fen ❯ 2 tre du second navigateur et entrez ceci dans la console Posts.find().count(); , strictement parlant : ' sans qu on ait ' nous Mais ce n est pas Console du second navigateur ' L article est ici aussi ! avec le second navigateur . mises à jour é ' C est arriv vident plus tard é Ce qui est arriv ' ' ê Quand bien m é , par magie – é et instantan ôé est que la collection c , é et nous avons certainement pas . d un nouvel article î t , ment aussi serveur a ' éé t et a pris en charge la tâche d ins ' bien que ça devienne plus é ôé inform ' é é é cup par la collection c t comment lier les donn , es dans nos templates simple prototype HTML en application web temps r é client é ' é t rer l article dans la base de donn è rer les articles dans la console du navigateur n est pas tr ô bient me interagit crit de code pour envoyer les post Mongo et de renvoyer l information à toutes les autres collections R ê me nous n avons jamais rafra chit ou m s utile . es é . connect es Nous apprendrons é et par cons quent modifier notre . el fonctionnelle Remplir la base de données Regarder le contenu de nos collections sur le navigateur est une chose ' é , aimerions vraiment faire c est afficher les donn l 'é . cran , en application web temps r é é . es é ' Nous allons faire ça avec un fichier d installation es structur è , es changeantes é re chose que nous allons faire est de mettre des donn é Premi é , et les changements de ces donn el avec des donn è La premi donn mais ce que nous es sur En faisant ça nous transformerons notre simple page web affichant du contenu statique donn es , , rement é es dans la collection Posts ( . es dans notre base de fixture ) é quand le serveur d ' ' dynamiques qui charge un ensemble de è marre pour la premi é . assurons nous qu il n y a rien dans la base de donn es Nous utiliserons meteor reset , qui vide votre base de données et remet à zéro votre projet. Bien sûr, vous é serez vraiment vigilant avec cette commande une fois que vous aurez commenc é sur des projets r ê Arr à travailler . els tez le serveur Meteor ( en faisant ctrl-c ) et ensuite , dans le terminal , é ex cutez : . re fois meteor reset é . La commande reset vide la base de donn il y a de fortes probabilit es é ' é C est une commande utile en d é s que votre base de donn Relançons notre application Meteor é es soit dans des , ù veloppement . tats inconsistants : meteor é Maintenant que la base de donn es est vide , vous pouvez ajouter le code suivant qui va é charger trois articles quand le serveur va d marrer si la collection Posts if (Posts.find().count() === 0) { Posts.insert({ title: 'Introducing Telescope', url: 'http://sachagreif.com/introducing-telescope/' }); Posts.insert({ title: 'Meteor', url: 'http://meteor.com' }); } Posts.insert({ title: 'The Meteor Book', url: 'http://themeteorbook.com' }); / . server fixtures js Commit 4-2 é Ajout de donn . es dans la collection posts Voir sur GitHub ' Lancer l instance est vide : o é Nous avons plac ' é ce fichier dans le r . navigateur d un utilisateur ' pertoire é Le code sera ex et fera des appels d insertion ( insert ) server/ , il ne sera donc jamais chargé dans le é é cut imm é diatement quand le serveur d é sur la base de donn marrera , es pour ajouter les trois articles Posts . dans notre collection meteor , et ces trois articles seront chargés dans la Maintenant relancez votre serveur avec é . base de donn es Des données dynamiques , Si nous ouvrons une console navigateur MiniMongo é nous verrons les trois articles charg s dans : ❯ Posts.find().fetch(); Console navigateur , Pour afficher ces trois articles dans le rendu HTML 3 Dans le chapitre ( contexte de donn nous utiliserons un template helper nous avons vu comment Meteor nous permettait de joindre un data context é structures de donn ) es à nos templates Spacebars pour construire des vues HTML de simple é . es é Nous pouvons joindre nos donn ê es de collections de la m Nous remplacerons simplement notre objet JavaScript statique dynamique . postsData . me façon par une collection . , En parlant de ça vous devrait ressembler ê tes libres de supprimer le code posts_list.js maintenant : postsData ' . à partir d ici Voici à quoi Template.postsList.helpers({ posts: function() { return Posts.find(); } }); / / / _ client templates posts posts . list js Commit 4-3 Affichage de collection dans le template ` `. postsList ' Voir sur GitHub Lancer l instance Chercher & Récupérer Dans Meteor é r . active fetch() ' A l int ' retourne un cursor é Quand nous voulons r curseur ), é qui est une source de donn , rer son contenu es nous pouvons utiliser . , rieur d une application Meteor est assez intelligent pour savoir comment ' . rer sur un curseur sans avoir à les convertir d abord explicitement en tableaux C est pourquoi vous ne verrez pas actuel Plut é cup ( sur ce curseur pour le transformer en tableau ' é é it , find() ( ' fetch() si souvent que ça dans le code Meteor ' - ). et pourquoi nous ne l utiliserons pas dans l exemple ci dessous ' ô , t que de prendre une liste d articles sous forme de tableau statique depuis une variable nous retournons maintenant un curseur de notre helper ' î é n appara tront pas vraiment diff é donn ): es posts ( bien que les choses remment puisque nous utilisons toujours les m ê mes Utiliser des données dynamiques Notre helper ôé c t {{#each}} é serveur a r éé cup r éé a it r , les articles de Mongo é les a envoy é nos helpers Spacebars les ont affich , Maintenant t , client et . ; ajoutons un autre article via la console : Posts.insert({ title: 'Meteor Docs', author: 'Tom Coleman', url: 'http://docs.meteor.com' }); ❯ Console navigateur – ôé s vers la collection c s dans notre template nous pouvons aller un peu plus loin Regardez dans le navigateur ' Posts , et les a affiché à l écran. La collection sur tous nos vous devriez voir ça : Ajout d'articles via la console é Vous venez juste de voir la r é activit é Spacebars qui it rait sur le curseur , changements sur le curseur 'é é donn es correctes à l è en action pour la premi . re fois Quand nous parlions de Posts.find() , il savait comment observer les é et a corrig le HTML de la plus simple façon pour afficher les . cran Inspecter les changements du DOM , Dans ce cas ' <div le plus simple changement possible est d ajouter un autre class="post">...</div> . Si vous voulez vous assurer du changement, ouvrez ' é l inspecteur du DOM et s <div> lectionnez le correspondant à un des articles . existants , Maintenant ' , , sur l inspecteur , article é dans la console JavaScript vous allez voir un <div> éé l ments ont me t . rez un autre article é suppl <div> ê éé mais vous aurez encore le m utile de voir quand les ins - , é mentaire existant s Quand vous revenez correspondant au nouvel é. éé lectionn re rendus et quand ils ont Connecter les collections : Publications et Souscriptions t ' C est un moyen é laiss . s seuls é Depuis le d , but . applications en production ê collection doit autopublish nous avons le paquet Comme son nom l indique é tre partag nous voulons vraiment , ' activ é e dans sa totalit é donc d Ouvrez un nouveau terminal , sactivons le et tapez , é, ' é qui n est pas conseill pour les ce paquet dit simplement que chaque é. à chaque client connect ' Ce n est pas ce que . : meteor remove autopublish ' L effet est instantan é. tous vos posts ont disparus ' , Si vous regardez dans votre navigateur maintenant ! ' C est parce que nous nous reposions sur ôé s assurer que la collection posts c t client vous verrez que autopublish pour é tait un mirroir de tous les articles dans la base de é . donn e é Eventuellement nous allons avoir besoin de nous assurer que nous transf ' articles que l utilisateur a besoin de voir ). pagination sa totalit é. ' Mais pour l instant , Pour faire cela les posts nous cr , ( en prenant en compte les choses comme la nous allons juste configurer é ons une fonction rons seulement les publish() Posts ' é pour qu il soit publi éé qui retourne un curseur r f dans rençant tous : Meteor.publish('posts', function() { return Posts.find(); }); / . server publications js , Dans le client . nous avons besoin de souscrire à la publication suivante au fichier main.js : Meteor.subscribe('posts'); Nous allons ajouter la ligne / . client main js Suppression de Commit 4-4 ` ` autopublish é , rifions le navigateur … ' Voir sur GitHub Si nous v ' et mise en place d une publi Lancer l instance nos articles sont de retour . Pfiou ! Conclusion ' - ' Donc qu est ce qu on a fait ? ' . nous avons maintenant est une application web fonctionnel , application sur Internet et ( ' Bien que nous n avons pas encore d interface utilisateur î ' ) ce que é Nous pourrions d en utilisant la console du navigateur , ployer cette commencer à poster des nouvelles histoires et les voir appara tre dans les navigateurs d utilisateurs partout à travers le monde . Publications et Souscriptions 4.5 SIDEBAR Publications et souscriptions sont un des concepts les plus importants et fondamentaux dans Meteor , mais peut ê é tre difficile à se repr ' Cela a conduit à beaucoup d incompr é senter quand on d é , hensions é les applications Meteor ne peuvent pas g bute . ' telles que Meteor n est pas s é rer une large quantit é é, curis ou que é . de donn es Une grande part de la raison pour laquelle les gens trouvent ces concepts un peu perturbants est la “ magie ” . que Meteor fait pour nous è elle peut obscurcir ce qui se passe vraiment en arri ). faire - re plan é D ' é Bien que cette magie est d une aide consid ( , rable comme la magie a tendance à le ' . taillons un peu les couches de magie pour essayer de comprendre ce qu il se passe Les vieux jours , Mais dans un premier temps 'é n tait pas encore là . , , re dans les vieux jours de é Disons que vous d utilisateur atteint votre site application è regardons en arri (. . le client . i e ) votre navigateur . ê envoie une requ é Le premier travail de l application est de trouver quelles donn Ça pourrait ê tre à la page , derniers tweets de Bob 12 . etc de vos r é sultats , Quand un te à votre ' . es l utilisateur a besoin de voir les informations de profil de Marie , 20 les Vous pouvez imaginer un marchand de livres recherchant dans é des all quand Meteor veloppez une simple application Rails qui est en cours sur votre serveur ' 2011 é. es le livre que vous avez demand Une fois que les bonnes donn traduire ces donn ). é es ont éé é t s é , lectionn es ' le second travail de l application est de é es dans un joli HTML lisible par un humain ( ' ou en JSON dans le cas d une API é Dans la m ' . d acheter et le mettre dans un joli sac - , taphore du marchand de livre ). View Controller ' vous pouvez envelopper le livre que vous venez juste C est la partie “ ” vue è du fameux mod le MVC ( - Model , Finalement ' ' l application est termin , virtuelles ' l application prend du code HTML et l envoie au navigateur é, Le travail de et maintenant que toutes ces choses sont sorties de ses mains é elle peut se d . è tendre avec une bi ê . re et attendre la prochaine requ te La vision Meteor é Regardons ce qui rend Meteor si sp é cl ' . cial en comparaison ' , de Meteor est que là ou l application Rails vit seulement sur le serveur Meteor inclut ). é ôé galement un composant c t ' , Comme nous l avons vu ' é client qui va s ex l innovation une application cuter sur le client ( le navigateur Envoyer un sous-ensemble de la base de données au client. ' C est comme un marchand qui non seulement vous trouve le livre chez vous pour vous le lire au moment de dormir ( , mais aussi vous suit jusque î ce qui peut para tre un peu bizarre à entendre ). Cette architecture laisse Meteor faire des choses sympathiques . que Meteor appelle database everywhere de votre base de donn é , é se d brouiller avec ça é donn é es instantan vers le serveur ( - Meteor va prendre un sous ensemble . es importantes : è premi , rement une application Meteor va envoyer la donn client parmi lesquelles la principale es et le copier sur le client é Cela implique deux id , Simplement , ( ). è data on the wire ê ment et m Deuxi ' au lieu d envoyer du code HTML au é , é e brute , mement v , ritablement et laisser le client vous allez pouvoir acc é der aux - me les modifier sans avoir à attendre un autre aller retour ). latency compensation Publier Une base de donn lesquels peuvent ' é ê é tre priv copier notre base de donn s ou sensibles é . ' Donc nous n allons , es sur le client é Par cons , es d application peut contenir des dizaines de milliers de documents ' é es peut . ê é tre envoy é , au client è videmment pas enti é pour des raisons de s quent nous allons avoir besoin d une m ensemble de donn é é curit ' rement é. et d adaptabilit - thode pour dire à Meteor quel sous ' ' ' et nous l effectuerons à l aide d une publication . Revenons à Microscope é : donn es ' Nous avons ici tous les articles de l application dans notre base de Tous les articles contenus dans la base de données. Bien que cette fonctionnalit que certains articles ont éé dans notre base de donn ). é envoy t é é signal é , es ' n est pas encore pr sente dans Microscope s pour langage abusif ils ne devront pas ê . , nous allons imaginer Bien que nous voulons les garder tre rendus disponibles aux utilisateurs (. . i e s au client è Notre premi . au client é : signal é s é re tâche sera de dire à Meteor quelles donn é es nous voulons r ellement envoyer Nous allons dire à Meteor que nous voulons seulement publier les articles non Exclure les articles signalés. , Voici le code correspondant é qui r sidera sur le serveur : // on the server Meteor.publish('posts', function() { return Posts.find({flagged: false}); }); ' ' ' ' é Cela assure qu il n y a aucun moyen possible qu un client soit capable d acc é. signal ' é C est exactement comme ça que vous allez s é vous juste que vous publiez seulement les donn der à un article curiser une application Meteor . es que vous voulez fournir au client : - assurez DDP , ù è Fondamentalement vous pouvez imaginer le syst comme un tunnel o transitent les donn ôé la collection c t client ( Le protocole qui est parl é ). ôé es de la collection c t serveur ( source ) vers cible é dans ce tunnel est appel , Pour en savoir plus sur DDP é DDP ( é ( un des cr ). Distributed Data Protocol é vous pouvez visionner cette pr time Conference par Matt DeBergalis vid / me de publication souscription - sentation de la Real é ateurs de Meteor ), ou cette capture é o par Chris Mather qui parcourt ce concept dans les moindres d . tails Souscrire ê Quand bien m me nous voulons rendre les articles non signal ' é . nous ne pouvons pas envoyer des milliers d articles en une fois é, donn ' ' é é é cifier aux clients quel sous ensemble de donn ' Nous avons besoin d un es ils ont besoin à un moment . et c est exactement là que les souscriptions interviennent Les donn l impl - é moyen de sp , s disponibles pour les clients é es auxquelles vous souscrivez seront dupliqu ôé mentation Meteor de MongoDB c , Par exemple t es sur le client grâce à Minimongo . client , disons que nous sommes en train de naviguer sur la page de profil de Bob nous voulons seulement afficher ses articles . et , Souscrire aux articles de Bob les copiera sur le client. è Premi , rement è nous modifierons notre publication pour prendre un param tre : // on the server Meteor.publish('posts', function(author) { return Posts.find({flagged: false, author: author}); }); é Et nous d è finirons ensuite ce param dans le code c ôé t tre au moment de la souscription à cette publication client de notre application : // on the client Meteor.subscribe('posts', 'bob-smith'); ' ' ôé C est de cette façon qu on rend une application Meteor adaptable c souscrire à toutes les donn . besoin , De cette façon é , es disponibles vous allez la taille de la base de donn é t viter les surcharges m serveur client : au lieu de on choisit les parties dont nous avons actuellement é ôé es c t . é moire du navigateur quelle que soit Trouver ' 'é Maintenant qu il est possible d exemple é en m “ Javascript ”, “ ' ” “ Ruby et é tiqueter les articles de Bob dans plusieurs cat ”), Python “ trouver ” ( par on pourrait avoir envie de charger tous les articles é moire mais de n afficher que ceux de la cat figure que gories gorie . “ ”. ' Javascript C est dans ce cas de intervient Sélectionner un sous-ensemble sur le client. ' , Comme nous l avons fait sur le serveur é s - nous utiliserons la fonction Posts.find() pour é . lectionner un sous ensemble de nos donn es // on the client Template.posts.helpers({ posts: function(){ return Posts.find({author: 'bob-smith', category: 'JavaScript'}); } }); é Maintenant que nous avons une bonne id , souscriptions e de quel r ô le jouent les publications et les creusons un peu plus profond et observons quelques patrons ' é d impl . mentations Autopublish é Si vous cr ez un projet Meteor à partir de rien automatiquement le paquet Le but de autopublish , application Meteor , le client autopublish è est de rendre tr (. . i e activ ) meteor create , il aura en utilisant é. Commençons par discuter de son r é s facile le d é but de d le veloppement de votre é et ce en dupliquant automatiquement toutes les donn es du serveur vers . ainsi en prenant soin des publications et des souscriptions pour vous Autopublish ô . Comment ça fonctionne . serveur ? autopublish Alors é 'posts' Supposez que vous avez une collection appel ' sur le enverra automatiquement chaque article qu il trouvera dans la é collection posts Mongo vers une collection appel ' e ). es 'posts' sur le client ( é en consid rant qu il y en a un é donn é é es sont omnipr ' ' autopublish , vous n avez pas besoin de penser aux publications. Les Donc si vous utilisez sentes , . et les choses sont simples è vidents d avoir une copie compl ' é te de la base de donn . û, Bien s ' r il y a des probl è mes es de l application en cache sur chaque machine d utilisateur Pour cette raison ' , é autopublish est seulement appropri n avez pas encore r é é fl é quand vous d , marrez et que vous . chi aux publications Publier des collections complètes é autopublish , Une fois que vous avez supprim é que toutes vos donn . es ont disparues du client copier ce que autopublish fait , vous allez rapidement vous rendre compte Une façon simple de revenir et simplement de é. et de publier une collection dans sa totalit Meteor.publish('allPosts', function(){ return Posts.find(); }); Par exemple : Publier une collection complète Nous publions encore des collections compl ô contr Posts è tes , mais au moins nous avons maintenant le le sur quelles collections nous publions ou pas mais pas . Dans ce cas , nous publions la collection Comments . Publier des collections partielles ô Le niveau de contr ' . le suivant est de publier seulement une partie d une collection exemple seuls les articles qui appartiennent à un certain auteur : Par Meteor.publish('somePosts', function(){ return Posts.find({'author':'Tom'}); }); Publier une collection partiellement En coulisses , Si vous avez lu la documentation de publication Meteor é d é pass ' added() par l utilisation de enregistrements sur le client é vous avez d ' , ready() et -ê vous avez peut é La raison est que Meteor fournit une m ' é . thodes thode vraiment tr ? è s pratique -ê Peut : ' – pour ' é vous l avez devin _publishCursor() é somePosts – a retourn , un curseur il appelle . publier ce curseur automatiquement : fait é Il v dans une c est exactement ce qu utilise Meteor Quand Meteor voit que la publication Voici ce que , tre pas directement ( . . Posts.find({'author':'Tom'}) ) ' . mais si vous retournez un curseur i e _publishCursor() t pour appliquer les attributs des _publishCursor() . Vous n avez jamais vu ça utilisé , éé et pour recoller ça avec les applications Meteor que jà vu et qui n utilise pas ces m fonction de publication tre ôé rifie le nom de la collection c t . serveur - Il prend tous les documents correspondants du curseur et envoie celui ci dans ôé une collection c t . ê client du m ' me nom ( é, A chaque moment qu un document est ajout curseur et ' ' - , . client et ( é supprim é, ou modifi .observe() Il utilise removed() pour faire ça ). il envoie sur le ). pour faire ça ' nous pouvons nous assurer que l utilisateur a seulement les articles qui l interessent . t .added() , .changed() Dans l exemple au dessus - ôé ces changements à la collection c .added() Il utilise ( ceux é crit par Tom ) ôé disponibles dans leur cache c t client Publier des propriétés partielles , Nous avons vu comment publier seulement certains articles affiner ! mais nous pouvons continuer à éé Voyons comment publier seulement certaines propri Juste comme avant , nous allons utiliser nous allons exclure certains champs : find() t é s sp . cifiques , pour retourner un curseur mais cette fois Meteor.publish('allPosts', function(){ return Posts.find({}, {fields: { date: false }}); }); Publier des propriétés partiellement û, Bien s r nous pouvons é . galement combiner deux techniques ôé retourner tous les articles de Tom en laissant de c t , Par exemple , leurs dates nous é si nous voulions cririons : Meteor.publish('allPosts', function(){ return Posts.find({'author':'Tom'}, {fields: { date: false }}); }); Résumé éé Donc nous avons vu comment publier chaque propri collection ( avec autopublish ) ' t de tous les documents de chaque éé jusqu à publier seulement certaines propri . t s de certains documents de certaines collections Ceci couvre les bases de ce que vous pouvez faire avec les publications Meteor ' é techniques s occuperont de la vaste majorit , Parfois ' , et ces simples . des cas d utilisation ' , vous aurez besoin d aller plus loin en combinant Nous allons en discuter dans un prochain chapitre ! , reliant . assemblant des publications Le Routage 5 Maintenant que nous avons une liste de posts par des utilisateurs auront la possibilit ), é ' ( qui peuvent avoir t é ventuellement envoy ù nous avons besoin d une page pour chaque post o de laisser des commentaires http://myapp.com/posts/xyz , . ( ù xyz o s les utilisateurs . Nous aimerions rendre ces pages accessible par un permalien pour chaque post é éé une URL de la forme est un identifiant MongoDB ' _id ) qui est unique ' Cela signifie que nous allons avoir besoin d un routage pour voir ce qu il y a dans la barre URL . du navigateur et afficher le contenu correspondant Ajout du package Iron Router Iron Router est un package de routage qui a Meteor é é éé cr é sp cialement pour les applications . ' Non seulement c est une aide pour le routage ' s occupe aussi des filtres abonnements é d t é velopp ( (' ( ) l assignation de ces chemins à des actions ' é savoir quel chemin permet d acc - ), la mise en place des chemins , par un des co auteurs de Discover Meteor e ) . Tom Coleman Commençons par installer le package depuis Atmosphere ê et il s occupe m )( é . der à quelle donn ' mais le package Note : me des Iron Router a éé t : meteor add iron:router Terminal Cette commande va t éé l é Notez que vous devrez probablement red , terminer le processus . charger et installer le package Iron Router dans votre application puis meteor marrer votre application Meteor é pour le red ) marrer ( avec ctrl+c avant que le package ne soit pour . utilisable Vocabulaire sur le routage é Nous allons aborder plusieurs fonctionnalit é avez d é jà utilis . ' : ' Si ce n est pas le cas ù : Segments : ( ) ou Path . ' . est une URL de l application ( / ). tre , é rentes parties qui composent un chemin é Les Hooks sont les actions qui seront effectu ' . , es avant cessaire pour afficher une page é Les filtres sont des hooks qui sont d è apr Un exemple typique serait de v é l utilisateur a les droits n : ê Il peut m é pendant le processus de routage Elle peut é s par s ou m Template de routes , cisez pas un : es ê me rifier si . finis globalement pour une ou . é me é plusieurs routes pr ê tres ce sont les diff par un slash Filtres ' ou dynamique y avoir des param : ' C est un jeu d instruction qui ( /information_legales ) ( /posts/xyz ). è ( /search?keyword=meteor ). statique : aller et quoi faire pour chaque URL un chemin voici un glossaire pour vous aider . dit à l application o Hooks , la route est le bloc de base du routage Chemins Si vous un Framework comme Rails vous connaissez probablement la plupart de ces concepts Routes . s du routage dans ce chapitre Chaque route doit pointer vers un template ê le routeur cherchera le template avec le m . ' Si vous n en me nom que la . route Layouts : é . Vous pouvez voir les layouts comme des cadres pour vos donn es Ils contiennent tout le code HTML qui entoure les templates et qui ne bougera pas ê m - ê me si le template lui m ô Contr leurs : , ê Quelques fois é templates r code , utilisent les m vous pouvez faire h é. me est modifi vous vous rendrez compte que beaucoup de mes param è é . tres ô Plut t que de dupliquer votre ' ê riter toutes ces routes d un m ô me contr leur de . routage qui contient toute la logique de routage ordinaire ' Pour plus d information sur Iron Router . , è consultez la documentation compl GitHub Routage : Relier des URLS à des templates te sur ' é Jusqu à pr ( comme é sent nous avons construit notre layout en utilisant des inclusions cod ) {{>postsList}} . Bien que le contenu de notre application puisse changer, la structure de la page est toujours la m ê me : . un titre avec une liste de posts en dessous é Iron Router nous laisse sortir du cadre en nous laissant changer ce qui est affich ' <body> . Donc nous n allons pas définir le contenu de cette balise nous HTML . dans une page HTML classique A la place , cial qui contient un helper de template Ce helper {{> yield}} me comme finir une zone dynamique qui va automatiquement afficher le template correspondant à la route courante maintenant ce template sp ê m {{> yield}} . é va d - dans la balise nous allons pointer le routeur vers un template é sp es en dur é cial le “ ( , par convention template de routage ”) : é nous d signerons à partir de Layouts et templates. Nous allons commencer par cr è Premi , rement é er notre layout et ajouter le helper nous allons supprimer l 'é é l ment HTML <body> de {{> yield}} . main.html , et déplacer , layout.html son contenu vers son propre template dossier ) client/templates/application . ' ' Iron Router s occupera d int ressemble maintenant à ça ( que nous placerons dans un nouveau é grer notre layout dans le template minimaliste main.html , qui : <head> <title>Microscope</title> </head> / . client main html Le fichier éé layout.html application nouvellement cr é contiendra maintenant le layout ext rieur de notre : <template name="layout"> <div class="container"> <header class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <a class="navbar-brand" href="/">Microscope</a> </div> </header> <div id="main"> {{> yield}} </div> </div> </template> / / / . client templates application layout html é Vous noterez que nous avons remplac helper è Apr ' yield . s ce changement . d Iron Router ' , ' ' l inclusion du template postsList ' avec un appel du é l onglet de notre navigateur affiche la page d aide par d ' faut ' C est parce que nous n avons pas encore dit au routeur que faire avec l URL . donc il renvoie un template vide /, é Pour d / , marrer au template é r pertoire ' nous pouvons retrouver notre ancien comportement en assignant l URL racine /lib postList . Nous allons créer un nouveau fichier router.js dans la racine du projet ' é à l int : rieur du Router.configure({ layoutTemplate: 'layout' }); Router.route('/', {name: 'postsList'}); / . lib router js é Nous avons effectu ' . deux choses importantes è Premi é d utiliser le layout que nous venons tout juste de cr , rement nous avons dit au routeur é er comme layout par d faut pour toutes . les routes è Deuxi , mement é assign é nous avons d e à la racine fini une nouvelle route appel é postList e ' et nous l avons /. Le répertoire /lib é Quoi que vous mettiez dans le r /lib , cela sera assurément chargé en pertoire premier avant tous les autres fichiers de votre application ). possible les paquets intelligents 'ê helper qui a besoin d dans : avec comme exception Ceci en fait une place de choix pour y mettre un tre disponible en permanence Une petite mise en garde ( notez que le r é . /lib pertoire ' n est ni dans /client ni /server , cela signifie que le contenu sera disponible dans les deux . environnements Routes nommées ' ïé Éclaircissons un peu l ambigu t avons é . ici galement un template appel é Nous avons nomm é postList . notre route ' - ' postList , mais nous Donc qu est ce qu il va se passer ici ? é Par d fait , , faut Iron Router va chercher un template avec le m ê il va m é me d ( puisque notre chemin est me nom que celui de la route é duire le nom du chemin que vous sp dans ce cas particulier ê ) . cifiez / , Iron Router aurait trouvé le bon é http://localhost:3000/postsList ê Vous pouvez vous demander pourquoi nous avons quand m . . comme chemin me besoin de nommer nos routes é Nommer les routes nous laisse utiliser quelques fonctionnalit é Iron Router qui nous rend plus facile la cr est le helper Spacebars . ation de liens dans notre application ' é ' cifier une URL statique ê sera le m me , s de La plus utile {{pathFor}} , qui retourne l URL du composant chemin de la route. ' , Nous voulons que notre lien d accueil principal pointe vers la liste d articles sp En Bien que cela ne marcherait pas template si nous avions utilis dans un premier temps . / , nous allons pouvoir utiliser le helper Spacebars. Le résultat final é mais cela nous donne plus de flexibilit ê toujours la bonne URL m donc au lieu de è me si nous changeons apr puisque le helper nous renverra - s coup le chemin de la route dans le . routeur <header class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <a class="navbar-brand" href="{{pathFor 'postsList'}}">Microscope</a> </div> </header> //... / / / . client templates application layout html Commit 5-1 è Routage tr Voir sur GitHub s basique . ' Lancer l instance Attente De Données é Si vous d ' ployez la version courante de l application ( ' ou lancez l instance web en utilisant le - ), . ' ' î lien au dessus apparaissent vous noterez que la liste appara t vide un petit moment avant que les articles è C est parce que quand la page se charge la premi afficher jusqu à que la souscription aux articles du serveur en train de se passer , ' ( ' , soit termin e r ' é cup é rant les donn es des é rience de fournir un indicateur visuel que quelque chose est . et que l utilisateur doit attendre un moment Iron Router nous donne un moyen facile de faire ça to wait on d attendre é , é articles ' il n y a pas d articles à . Ce serait une bien meilleure exp Par chance , re fois ) la souscription nous pouvons lui demander . é On commence par d : placer notre souscription posts main.js depuis vers le routeur : Router.configure({ layoutTemplate: 'layout', waitOn: function() { return Meteor.subscribe('posts'); } }); Router.route('/', {name: 'postsList'}); / . lib router js , Ce que nous voulons faire ici , pour le moment La principale diff souscription ), supprimer ' ' c est que pour chaque route du site ô mais nous en aurons bient t plus !), rence entre ceci et ce que nous avions pr tait dans é ' éé c demment posts . ( lorsque la main.js , qui devrait être dorénavant vide et que vous pouvez est que maintenant lorsqu elle a les donn ' nous n en avons qu une nous voulons souscrire à é é ( , Iron Router “ ” sait quand la route est pr . ê te –' -- c est à dire es dont il a besoin pour le rendu Visez un peu ça Savoir quand la route ' postsList ' est pr ê te ne nous est pas grandement utile si de toute façon nous n allons afficher qu un template vide ' ' . , Heureusement ' é é Iron Router inclut une proc ' pour retarder l affichage d un template jusqu à ce que la route qui l appelle soit pr ê , te d et affiche un template de chargement ( loading ) à la place : Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', waitOn: function() { return Meteor.subscribe('posts'); } }); Router.route('/', {name: 'postsList'}); / . lib router js é Notez que puisque nous d , ' é routeur ' finissons notre fonction cette s , quence ne se produira qu une fois l application pour la premi è re fois ' . è Apr s cela , waitOn ' globalement au niveau du é les donn è . ce finale du puzzle est le template de chargement pour cr é er un joli indicateur de chargement anim é et cr chargement ez le template de é. , comme suit é es seront charg du navigateur et le routeur n aura plus besoin de les attendre La pi é lorsqu un utilisateur acc dera à es dans la m é moire . Nous allons utiliser le paquet Ajoutez le avec spin meteor add sacha:spin , dans le dossier client/templates/includes : <template name="loading"> {{>spinner}} </template> / / / . client templates includes loading html Notez que partial “ {{>spinner}} ” ne provient pas . est un partial contenu dans le paquet , de notre application ' spin . Quand bien même ce ' nous pouvons l inclure comme n importe quel autre template ' é C est normalement une bonne id ' é l exp rience utilisateur , ' , e d attendre les souscriptions non seulement pour , mais aussi parce que cela signifie que vous pouvez partir du principe que les donn é , avec certitude es seront toujours disponibles depuis un template . Cela é supprime le besoin de g - ù éé rer les cas o , les templates sont interpr é sous jacentes soient disponibles ce qui n t é s avant que leur donn es . cessite souvent des astuces laborieuses Commit 5-2 . Attendre la souscription aux articles ' Voir sur GitHub Lancer l instance Un premier aperçu sur la réactivité La r é é ativit , est une partie essentielle de Meteor é, encore vraiment touch ' et bien que nous n y avons pas notre template de chargement nous donne un premier . aperçu de ce concept é Rediriger vers un template de chargement si les donn , est vraiment bien ' , . - restons en là ' es mais comment le routeur sait quand rediriger l utilisateur vers la bonne page une fois que les donn Pour l instant é es ne sont pas encore charg é es arrivent ? ' ù disons juste que c est exactement o é Mais ne vous inqui tez pas , la r é é activit , intervient ô vous en apprendrez plus bient t et ! Router vers un article spécifique Maintenant que nous avons vu comment router vers le template route pour afficher le d ' ' é postsList , ajoutons une . tail d un seul article ' Il n y a pas qu un seul article : il y en aurait des milliers . ' é nous ne pouvons continuer et d , ' sinon , Donc nous allons avoir besoin de mettre une seule route dynamique ' ' et permettre à la route d afficher n importe quel article que l on souhaite Pour commencer , finir une route par article é nous allons cr d article que nous avons utilis é . ê er un template qui renvoie simplement le m ' . dans la liste d articles me template <template name="postPage"> <div class="post-page page"> {{> postItem}} </div> </template> / / / _ client templates posts post . page html 'é é Nous allons ajouter plus d ' l ments dans le template plus tard ( mais pour l instant il va simplement servir de coquille pour notre inclusion Nous allons cr é é , er une autre route nomm /posts/<ID> forme au template e ), tels que les commentaires postItem . ' cette fois en associant les chemins d URL de la postPage : Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', waitOn: function() { return Meteor.subscribe('posts'); } }); Router.route('/', {name: 'postsList'}); Router.route('/posts/:_id', { name: 'postPage' }); / . lib router js La syntaxe sp ' é ciale :_id dit au routeur deux choses n importe quelle route de la forme è Deuxi , mement tableau des : ' /posts/xyz/ , où è premi “ ” xyz , rement peut ê faire correspondre ' . tre n importe quoi é é _id mettre ce qu il trouve à la place de xyz dans une propri params du routeur t dans le . Notez que nous utilisons seulement _id connaitre si ce que vous lui passez est un . par convention ici ' Le routeur n a pas de moyen de _id , ou juste une chaîne aléatoire de caractères. , Nous routons maintenant vers le template correct mais il nous manque encore quelque chose : î ' . toujours pas d indice , Heureusement é ' , - é Donc comment peut on combler ce foss ' é es es comme l int simplement de l article que nous voulons afficher é é le routeur a une solution int contexte de donn donn ’ _id le routeur conna t l ' é ( data context ' é rieur d un d ) gr e intelligente de template . : ? , ' mais le template n a é il vous laisse sp cifier un Vous pouvez imaginer le contexte de licieux gateau fait de templates et de layouts c est ce avec quoi vous remplissez votre template . Tout : Le contexte de données. , Dans notre cas é bas ’ _id sur l nous pouvons r é r cup éé r é ' cup dans l URL é : é rer le bon contexte de donn es en regardant notre article Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', waitOn: function() { return Meteor.subscribe('posts'); } }); Router.route('/', {name: 'postsList'}); Router.route('/posts/:_id', { name: 'postPage', data: function() { return Posts.findOne(this.params._id); } }); / . lib router js ' è A chaque fois qu un utilisateur acc . passerons au template ê , à la requ ' te rieur de la fonction , correspondante la route ( data _id é ' é nous trouverons l article appropri ' , this d une route é {_id: id} . correspond à la route courante this.params en les pr et le retourne un seul article qui correspond comme argument est un raccourci pour et nous pouvons utiliser que nous avons indiqu , findOne Souvenez vous que et que fournir juste un é A l int - de à cette route fixant avec : é pour acc der aux parties nomm ). dans notre chemin é es de En savoir plus à propos des contextes de données En initialisant un contexte de donn this de é es de template dans les helpers de template ' é automatiquement le contexte de donn ' é ration ô nous pouvons contr ler la valeur . ' é C est habituellement fait implicitement avec l it d it , {{#each}} , qui renvoie rateur é es de chaque it ' ration à l item en cours : {{#each widgets}} {{> widgetItem}} {{/each}} Mais nous pouvons simplement pouvons é “ é galement le faire explicitement en utilisant prends cet objet crire , {{#with}} , qui dit ”. et applique lui le template suivant : , Par exemple nous {{#with myWidget}} {{> widgetPage}} {{/with}} ' è Il s av re que vous pouvez obtenir le m ' . argument dans l appel de template comme suit : ê é me r sultat en passant le contexte comme Et donc le code pr éé c dent peut ê tre r éé crit {{> widgetPage myWidget}} é Pour une exploration plus pouss lire notre article de blog ( é e des contextes de donn ) en anglais é es nous vous sugg rons de . sur ce sujet En utilisant un Route Helper Dynamique Nommé , Enfin nous allons cr é er un nouveau bouton . personnelle de posts De m ê , me “ Discuter ” qui redirigera vers notre page nous pourrions faire quelque chose comme ' href="/posts/{{_id}}"> , mais c est plus fiable en utilisant un route helper. <a é Nous avons nomm postPage , donc nous pouvons utiliser le helper la route article {{pathFor 'postPage'}} : <template name="postItem"> <div class="post"> <div class="post-content"> <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3> </div> <a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Discuss </a> </div> </template> / / / _ client templates posts post . item html Commit 5-3 ' . Route vers la page d un article ' Voir sur GitHub Attendez , è Apr ' comment le routeur sait comment r , s tout è Il s av nous ne lui passons aucun é é cup rer la partie xyz ' ( _id . - ' ê me . é _id Donc le routeur cherchera cet fini notre ) de ce path . ' dans l endroit disponible le plus logique ' !) ? Nous disons au postPage , et le routeur sait que cette route requiert un _id vu que c est comment nous avons d du helper /posts/xyz dans re que Iron Router est assez intelligent pour le trouver par lui m routeur d utiliser la route type Lancer l instance : le data context {{pathFor postPage}} , en d autre mots this . Et il se trouve que notre this ' , correspondre à l article Alternativement , lequel vous pouvez é é _id , cherche la propri t ( surprise poss è é é _id . de une propri t é ù galement explicitement dire au routeur o en passant un second argument au helper ) va ' vous aimeriez qu il ( . . {{pathFor i e 'postPage' someOtherPost}} . Un usage pratique de ce modèle serait de récupérer le lien éé des articles pr c dents et suivants dans une liste Pour voir si ça fonctionne correctement liens ‘ ’. Discuss , , par exemple . ' naviguez dans la liste d articles et cliquez sur un des Vous devriez voir quelque chose comme ça : Page d'un article. HTML5 pushState ' 5 . Une chose à savoir est que ces changements d URLs utilisent HTML é Le routeur r è cup ' 'é é de naviguer à l ext é n cessaires à l , re les clics sur les URLs internes au site ' , rieur de l application ' pushState ê et emp che le navigateur en plus de faire les changements . tat de l application é Si tout fonctionne correctement la page devrait changer instantan ' . ment parfois les choses changent si vite qu une sorte de transition pourrait ' , C est hors du champ de ce chapitre Article non trouvé ê mais un sujet tout de m é me int ê , En fait é . tre n ressant . cessaire ' N oublions pas que le routing fonctionne dans les deux sens ' , lorsqu on visite une page , Ainsi : ' il permet de changer l url ' ' . mais il peut aussi afficher une nouvelle page lorsqu on change l url ' . nous devons nous assurer de ce qui se passe si quelqu un entre une mauvaise url , Heureusement , En premier lieu ' ' ' Iron Router s occupe de cela pour nous grâce à l option notFoundTemplate . nous allons mettre au point un nouveau template qui affiche un simple message d erreur 404 : <template name="notFound"> <div class="not-found page jumbotron"> <h2>404</h2> <p>Désolé, nous ne pouvons pas trouver une page à cette adresse.</p> </div> </template> / / / _ client templates application not , Ensuite . found html nous allons tout simplement lier Iron Route à ce template : Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return Meteor.subscribe('posts'); } }); //... / . lib router js ' Pour tester notre nouvelle page d erreur comme , ' http://localhost:3000/rien-par-ici . Un instant ; -- ' é vous pouvez essayer d acc que se passe t il si quelqu un entre une url de la forme http://localhost:3000/posts/xyz , où xyz ’ n estpas un _id der à une url quelconque ' valide d article ? ' C est , toujours une route valide , Heureusement é sp cial è mais elle ne m é . ne à aucune donn é Iron Router est assez intelligent pour g dataNotFound à la fin de e rer cela , ' il suffit d ajouter un hook router.js : //... Router.onBeforeAction('dataNotFound', {only: 'postPage'}); / . lib router js ' Cela indique à Iron Router d afficher la page “ non trouv data invalides mais aussi à chaque fois que la fonction null , false , undefined ou un objet vide ). é ” non seulement pour les routes Commit 5-4 Avec le template « Voir sur GitHub non trouv ( é é . . renvoie un objet non d sir i e » é. ' Lancer l instance Pourquoi “Iron" ? ù Au cas o ' è d apr éé m t ' ' è vous vous demanderiez qu elle est l histoire derri , é é s Chris Mather cr ores sont compos ateur de Iron Router , ' re le nom " Iron Router cela s explique par le fait que les es principalement de fer ( iron en anglais ). ": La Session 5.5 SIDEBAR Meteor est un framework r é . actif é Ce qui veut dire que comme les donn , es changent les choses dans votre application changent sans que vous ayez explicitement besoin de faire quoi que ce . soit Nous avons d é donn é jà vu ça en action lors des changements de template à la modification de . es et de routes , Nous irons un peu plus loin dans le fonctionnement plus tard dans les chapitres , moment ê extr nous aimerions pr é é é senter quelques fonctionnalit s r mais pour le actives de base qui sont é é . mement utiles dans les applications en g n ral La Session Meteor ' ' ' , é Actuellement dans Microscope ' l contenu dans l URL qu il regarde , Mais dans de nombreux cas ( tat courant de l application utilisateur est compl é et la base de donn ). ' éé l ment soit affich é vous allez avoir besoin de stocker des ) é. ou cach ' singleton global : il y a une session , é ' es r é active . ' é tats é é è ph m ' , , par exemple . al pour faire ça ' . Les variables globales sont mais dans ce cas la session peut é utilis ( res qui sont C est global dans le sens d un objet et c est accessible partout habituellement vues comme des mauvaises choses ' é La session est un moyen id La session est une zone de stockage de donn tement es seulement pertinants pour la version courante de l utilisateur de l application qu un è é e comme un bus de communications central entre les diff ê tre rentes parties de . l application Changer la Session ' La Session est disponible partout sur le client en tant que l objet , valeur de sessions vous pouvez appeler : Session . Pour insérer une ❯ Session.set('pageTitle', 'A different title'); Console du navigateur é Vous pouvez ensuite lire les donn é source de donn ' es r é , active , et ça signifie que si vous la mettez dans un helper changer ce qu affiche le helper de mani , Pour essayer ça ' Session.get('mySessionProperty'); . C est une es avec è re r vous pouvez é . active en changeant la valeur de Session ajoutez le code qui suit au template layout : <header class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <a class="navbar-brand" href="{{pathFor 'postsList'}}">{{pageTitle}}</a> </div> </header> / / / . client templates application layout html Template.layout.helpers({ pageTitle: function() { return Session.get('pageTitle'); } }); / / / . client templates application layout js Une note sur le code des apartés é Notez que le code pr . , principal du livre vous utilisez Git ) sent é é dans les chapitres apart ' , les variables de Session . , Sinon er une nouvelle branche maintenant ( si . soit vous assurez d annuler les changements à la fin de ce chapitre Le rechargement automatique de Meteor navbar é Vous pouvez donc soit cr s ne fait pas partie du cours ( connu comme “ donc nous devrions voir maintenant éé retapez juste la pr c dente commande ” hot code reload “ ” A different title Session.set() ) ou HCR é pr é affich serve dans la . une nouvelle fois , De plus si nous changeons la valeur une fois de plus é devrions voir le titre affich ( via la console du navigateur changer une nouvelle fois : ), nous Session.set('pageTitle', 'A brand new title'); ❯ Console du navigateur La Session est disponible globalement ù o ' dans l application ' . donc de tels changements peuvent , Ça nous donne un peu de pouvoir si c est trop souvent utilis ' , mais ça peut ê galement . , ê è tre un pi ge é. me entre les onglets nouvel onglet ' tre fait n importe é ' ’ ' é D ailleurs il est important d indiquer que l objet Session n est pas partag ou m ê ' ' , entre les utilisateurs C est pourquoi si vous ouvrez maintenant l application dans un vous serez face à un titre de site vide . Changements Identiques Si vous modifiez une variable de Session avec ' , l initialisez à une valeur identique î é cha ne r , active et Session.set() mais que vous Meteor est assez intelligent pour passer outre la é é vitez les appels de fonction non n . cessaires Présentation de Autorun é Nous avons regard ' action à l int é ' é un exemple d une source de donn ' rieur d un helper de template ) template helpers é sont r è actifs de mani . ù Mais là o é re inh Meteor est encore plein de JavaScript non r é rentes . , es r é , active ' les contextes dans Meteor é la majorit é et nous l avons observ ' ( en tels que les du code de l application actif Supposons que nous avons le petit bout de code suivant quelque part dans notre application : helloWorld = function() { alert(Session.get('message')); } ê Quand bien m é appel , me nous appelons une variable de Session é e est non r actif , ' le contexte dans lequel elle est alerte ce qui signifie que nous n aurons pas de nouvelle . à chaque fois que nous changerons la variable ' C est là que Autorun intervient autorun é r ' é s ex . ' é cutera et restera en cours d ex é actives utilis ' es à l int ' Comme son nom l indique é , ' é le code à l int ' rieur d un bloc é cution chaque fois que les sources de donn es . rieur changeront : Essayer de taper ça dans la console de votre navigateur Tracker.autorun( function() { console.log('Value is : ' + Session.get('pa geTitle')); } ); Value is : A brand new title ❯ Console du navigateur , Comme vous pouviez vous y attendre 'é é s x titre , cute une fois ' é le bloc de code fourni à l int é retournant ses donn es à la console . rieur du , Maintenant autorun essayons de changer le : ❯ Session.set('pageTitle', 'Yet another value'); Value is : Yet another value Console du navigateur ' C est magique ! é, autorun Comme la valeur de session a chang , contenu une nouvelle fois Revenons à notre pr éé c ' a su qu il devait executer son . renvoyant la nouvelle valeur à la console , dent exemple é si nous voulons d clencher une nouvelle alerte à , chaque fois que la variable change ' ' tout ce dont nous avons besoin est d envelopper notre autorun : code d un bloc Tracker.autorun(function() { alert(Session.get('message')); }); Comme nous venons de le voir é donn é es r actives et r é agir , autorun peut ê tre vraiment utile pour traquer les sources de . Hot Code Reload é Durant notre d veloppement de Microscope Meteor qui fait gagner du temps , fichiers de code Meteor d ' é nous avons profit hot code reload é ( ). HCR é cution , ' é d une fonctionnalit de Quand nous sauvegardons un de nos é tecte les changements et red serveur meteor en cours d ex ' : , marre de façon transparente le en informant chaque client de recharger la page C est identique à un rechargement automatique de la page , é mais avec une diff . rence . importante ' Pour trouver ce que c est é : utilis , commencer par r é initialiser la variable de session que nous avons e ❯ Session.set('pageTitle', ❯ Session.get('pageTitle'); 'A brand new title'); 'A brand new title' Console du navigateur Si nous devions recharger la fen ê session seraient naturellement perdues autre c ô é, t é si nous d fichiers source ) , tre de notre navigateur manuellement ( vu que nous cr clenchons un hot code reload la page se rechargera , ( é nos variables de ). ' erions une nouvelle session par exemple , D un en sauvegardant un de nos é . mais la variable de session sera encore initialis e Essayez ça maintenant ! Session.get('pageTitle'); 'A brand new title' ❯ Console du navigateur ' Donc si nous utilisons des variables de session pour tracer exactement ce que l utilisateur est , en train de faire le HCR devrait ê ' tre transparent pour l utilisateur . valeur de toutes les variables de session é Ceci nous permet de d ' , é comme ça pr serve la ployer en production des nouvelles versions de notre application Meteor avec l assurance que nos utilisateurs seront é d é rang é Consid . s de façon minimale . , la session ' Si nous pouvons g rer de garder tous les ' é nous pouvons changer le code source en cours d ex è l application de mani ' é tats dans l URL et cution de chaque client de . re transparente et avec un impact minimal ' é V é rez ceci pour le moment î rifions maintenant ce qu il se passe quand nous rafra chissons la page manuellement : Session.get('pageTitle'); null ❯ Console du navigateur , Quand nous rechargeons la page . nous perdons la session session en local dans le navigateur et la recharge apr , Cependant , é é initialis 'é à l ' ' c est comme s il avait encore explor tat de d é ' Meteor sauvegarde la s le rechargement de la page le comportement alternatif d un rechargement a du sens recharge la page r ' è , Dans un HCR : é ê la m , me URL . si un utilisateur et il devrait ê tre ' part que n importe quel utilisateur devrait voir en visitant l URL Les leçons importantes dans tout ça sont . : 1. 'é Toujours stocker l ' tat utilisateur dans la Session ou dans l URL afin que les utilisateurs é ne soient pas d 2. Stockez chaque ' ' é l int é rang é . s lors du Hot Code Reload tat que vous souhaitez mettre en commun entre les utilisateurs à - ê rieur de l URL elle m me . , Cela conclut notre exploration de Session ' ' . une des fonctions les plus utiles de Meteor N oubliez pas d annuler tous les changements de votre code avant de passer au chapitre . suivant Ajouter des Utilisateurs é Depuis le d , but nous avons appris à cr style particulier et on a tout int Nous avons m é donn é paralys é é gr é é er et afficher des donn dans un prototype simple ê me vu comment notre UI est r éé es ins 6 r es ou modifi é ' . é , active aux changements de donn é é es apparaissent imm é par le fait que nous ne pouvons pas ins pas encore d utilisateurs es statiques brutes dans un ! . diatement Par contre é . rer de donn es , En fait , es et les notre site est ' ê nous n avons m me . Voyons comment corriger ça Comptes: création d'utilisateurs faciles , Dans la plupart des frameworks web classique . , Bien sur , vous devez faire ça dans presque tous les projets aussi simple que ça le devrait syst ' è . , , Heureusement ôé é é pouvons b n ' t serveur ficier d un syst è ( d s que vous devez interagir avec OAuth ou un les choses ont tendance à se compliquer é . Meteor a tout pr contribuer au code c vu , Bootstrap é inqui ) ) JavaScript , ôé et c ' é é gr é é velopp ( , JavaScript HTML et CSS . ), nous rence est le style ). ( meteor avec toute notre application avec ian:accounts-ui-bootstrap-3 , En ligne de commande meteor add ian:accounts-ui-bootstrap-3 meteor add accounts-password Terminal client e de Meteor pour les comptes é mais comme nous avons d la seule diff t me de compte presque sans effort nous utiliserons le paquet tez pas . Grâce à la façon dont les paquets Meteor peuvent Nous pourrions simplement utiliser l UI int add accounts-ui ' mais ce n est jamais , è De plus mes d authentification tiers é ajouter des comptes utilisateurs est une fonctionnalit à la place ( ne vous nous tapons : è Ces deux commandes nous donnent acc é s aux templates sp pouvons les inclure dans notre site en utilisant le helper ô vous pouvez contr ( align ôé ler de quel c par exemple t ' ê s affiche la fen ' ciaux des comptes -ê te . ' tre d authentification en utilisant l attribut ) -ê Et comme cet en t donnons lui un peu de place avec son propre template ) et nous {{> loginButtons}} . Une astuce: {{> loginButtons align="right"}} . Nous ajouterons les boutons à notre en t , ( te commence à grossir nous le mettrons dans client/templates/includes/ . Nous utilisons également des balises externes et des classes é sp cifi é es par Bootstrap pour nous assurer que tout soit beau : <template name="layout"> <div class="container"> {{>header}} <div id="main"> {{> yield}} </div> </div> </template> / / / . client templates application layout html <template name="header"> <nav class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="col lapse" data-target="#navigation"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="{{pathFor 'postsList'}}">Microscope</a> </div> <div class="collapse navbar-collapse" id="navigation"> <ul class="nav navbar-nav navbar-right"> {{> loginButtons}} </ul> </div> </nav> </template> / / / . client templates includes header html Maintenant ' , , quand nous naviguons dans notre application nous voyons les boutons . d authentification en haut à droite dans le coin du site Interface intégrée des comptes utilisateur de Meteor ' ' , On peut utiliser ça pour s enregistrer ' , s authentifier , changer son mot de passe et tout ce . qu un simple site comportant des comptes utilisateur a besoin ' è Pour indiquer au syst ' , un nom d utilisateur un nouveau fichier me de compte que nous voulons que les utilisateurs s authentifient avec nous ajoutons simplement un bloc de configuration config.js ' é à l int rieur de client/helpers/ : Accounts.ui.config({ passwordSignupFields: 'USERNAME_ONLY' }); / / . client helpers config js Accounts.ui dans Comptes ajout é Commit 6-1 é é s et template int gr ' -ê à l en t te ' Voir sur GitHub Lancer l instance Création de notre premier utilisateur Enregistrez un compte : le bouton “ ” Sign In confirme que le compte utilisateur a bien de ce compte ? é é tre acc tapez d . é é éé t cr pour vous . 'ù Mais d o Ça é viennent les donn es En ajoutant le paquet ê ' changera pour afficher votre nom d utilisateur e avec accounts , Meteor a créé une nouvelle collection spéciale, laquelle peut Meteor.users() . Pour la voir, ouvrez la console de votre navigateur et : ❯ Meteor.users.findOne(); Console du navigateur La console retournera un objet correspondant à votre objet utilisateur è, plus pr s . vous identifie é connect Notez que vous pouvez - é , galement r é é cup ' Si vous regardez de avec un _id - . devrait retourner un second utilisateur Meteor.users.find().count(); Console du navigateur unique qui rer l utilisateur actuellement connectez vous et enregistrez vous avec un compte utilisateur diff Meteor.user() 1 é Meteor.user() . avec Maintenant d ❯ ' vous pouvez voir que votre nom d utilisateur est dedans . , Mais attendez é ex é . rent cutons : La console retourne é supprim ' ? 1. - Ne devrait il pas y en avoir 2 ? -- Le premier utilisateur a t il , SI vous essayez de vous authentifier avec le premier utilisateur éé t vous verrez que ce . n est pas le cas - é Assurons nous en et v é donn é v es Mongo rifiera . é rifions dans la zone de stockage des donn On se connectera à Mongo : ( meteor mongo es canonique , la base de ) dans votre terminal et on > db.users.count() 2 Console Mongo . Il y a bien deux utilisateurs - ' Donc pourquoi ne pouvons nous en voir qu un dans le navigateur ? Une publication mystère ! Si vous repensez au chapitre nous avons arr êé t ' 4, l envoi des donn collection des clients connect ' é autopublish , vous pouvez vous souvenir qu on a supprim é. s é es des collections du serveur vers chaque version local de Nous avions besoin de cr é er un couple publication é . souscription afin de faire transiter les donn ' Mais nous n avons pas encore pouvons quand m ' ' tabli de publication utilisateur é me voir une donn e utilisateur é La r es é ê ? é. s authentifier sur le site ! ' . 'ù Donc d o vient que nous - é ponse est que le paquet comptes auto publie les d l utilisateur actuellement authentifi / tails de base du compte de S il ne le faisait pas , ' l utilisateur ne pourrait pas ' . Le paquet comptes publie seulement l utilisateur courant é utilisateur ne peut voir les d ' tails d un autre compte Ceci explique pourquoi un . é La publication publie donc seulement un objet utilisateur par utilisateur authentifi ( et aucun 'ê si vous n , De plus ) é. tes pas authentifi les documents ne semblent pas avoir les m , Dans Mongo ê é . un utilisateur a beaucoup de donn Mongo et tapez . mes champs sur le serveur et sur le client es , Pour les voir retournez sur votre terminal : > db.users.find() { "_id": "H5kKyxtbkLhmPgtqs", "createdAt": ISODate("2015-02-10T08:26:48.196Z"), "profile": {}, "services": { "password": { "bcrypt": "$2a$10$yGPywo3/53IHsdffdwe766roZviT03YBGltJ0UG" }, "resume": { "loginTokens": [{ "when": ISODate("2015-02-10T08:26:48.203Z"), "hashedToken": "npxGH7Rmkuxcv098wzz+qR0/jHl0EAGWr0D9ZpOw=" }] } }, "username": "sacha" } Console Mongo ' Et de l autre c ô é, t ' , dans le navigateur l objet utilisateur est bien moins rempli pouvez le voir en tapant la commande é quivalente comme vous : Meteor.users.findOne(); Object {_id: "kYdBd9hr3fWPGPcii", username: "tmeasday"} ❯ Console du navigateur Cet exemple nous montre comment une collection local peut de la vrai base de donn é n é . es ' cessaires à son bon fonctionnement , apprendre é L utilisateur authentifi ( , . é tre un sous ensemble s é curis e voit seulement les informations dans ce cas vous vous en apercevrez plus tard - ê ' ). ' l authentification C est un cas utile pour é Ça ne veut pas dire que vous ne pourrez rendre publiques des donn souhaitez . éé Vous pouvez vous r f es utilisateur si vous le rer à la Documentation Meteor pour savoir comment vous pouvez publier plus de champs dans la collection Meteor.users . La Réactivité 6.5 SIDEBAR Si les collections sont la fonctionnalit rend utile é , principale de Meteor é la R é activit est la coquille qui la . Les Collections transforment radicalement la façon dont votre application traite les modifications de donn manuellement ( é . es Plut ô ' t que d avoir à v , par un appel AJAX par exemple ) é é rifier les modifications de donn es , et de les mettre à jour dans votre page HTML Meteor applique automatiquement ces modifications à votre interface utilisateur de façon transparente . Prenez un moment pour y penser : , dans les coulisses ' Meteor est capable de changer toute - . partie de votre interface utilisateur chaque fois qu une collection sous jacente est mise à jour ' .observe() , une fonction de curseur qui La meilleure façon de parvenir à cela serait d utiliser é d . clenche des callbacks lorsque des documents correspondant à ce curseur changent pourrions alors faire des changements dans le DOM . travers ces callbacks Le code r é ( ) le rendu HTML de notre page Web sultant ressemblerait à quelque chose comme ceci : Nous à Posts.find().observe({ added: function(post) { // quand le callback 'added' est déclenché, ajout de l'élément HTML $('ul').append('<li id="' + post._id + '">' + post.title + '</li>'); }, changed: function(post) { // quand le callback 'changed' est déclenché, modification du texte de l 'élément HTML $('ul li#' + post._id).text(post.title); }, removed: function(post) { // quand le callback 'removed' est déclenché, suppression de l'élément H TML $('ul li#' + post._id).remove(); } }); é Vous pouvez probablement d jà voir comment le code va rapidement se complexifier . ' Imaginez comment traiter les modifications de chaque attribut de l article ' du HTML complexe à l int é rieur des ' <li> . de l article é qui peuvent survenir quand nous commençons à g , et devoir changer ' Quand el -nous utiliser observe() ? devrions - è Utiliser le mod ' é le ci dessus est parfois n . s interfacer avec des gadgets tiers ' d une Collection , é ( , , , é sp cialement quand on doit imaginons que nous voulions ajouter é el des marqueurs bas é s sur des donn pour afficher la localisation des utilisateurs authentifi ' vous aurez besoin d utiliser des callbacks observe() é discuter la carte avec la collection Meteor et savoir comment r changements de donn removed es ) é . disons Dans certains cas cessaire Par exemple ou supprimer sur une carte en temps r carte s rer de multiples sources d information qui é . peuvent toutes changer en temps r é Sans parler de tous les cas compliqu é . es , Par exemple pour appeler les propres m afin de faire agir avec les vous pourriez utiliser les callbacks é dropPin() thodes et s removePin() added ' et de l API . Une approche déclarative Meteor nous fournit un meilleur outil é d . clarative ' Être d : la r é é é claratif nous laisse d é, qu ils resteront synchronis s é, activit qui est dans sa structure une approche finir la relation entre les objets une fois et savoir é au lieu de devoir sp cifier les comportements pour tous les . changements potentiels Ceci est un concept puissant , peuvent changer à de façon impr é affichons le HTML bas ' è parce qu un syst é visible . é me temps r é En exposant d é sur les sources de donn è surveiller ces sources et accomplir de mani ' es r é ' el a beaucoup d entr é es qui clarativement la façon dont nous , actives que nous observons Meteor peut re transparente ce travail de mise à jour . permanente de l interface utilisateur ' é é Tout ceci pour dire qu au lieu de r 'é d crire : fl chir sur des callbacks observe() , Meteor nous permet <template name="postsList"> <ul> {{#each posts}} <li>{{title}}</li> {{/each}} </ul> </template> é Et ensuite r ' é cup rer notre liste d articles avec : Template.postsList.helpers({ posts: function() { return Posts.find(); } }); è En arri - , re plan Meteor d é clenche des callbacks é sections pertinentes du HTML quand les donn , observe() es r é pour nous é et red ssine les . actives changent Surveillance de dépendance dans Meteor: Calculs (Computations) é , é Alors que Meteor est un framework temps r ' application Meteor n est pas r ' é . actif chaque fois qu il y a un changement , de votre code ' sources de donn active é r ( , , tait le cas À la place , la r ' é tout le code à l int é é activit é est limit ' rieur d une è votre application enti é é un calcul est un bloc de code qui est ex é es r , par exemple active , actif re se rechargerait à é e à des zones sp cifiques . , é . r et nous appellerons ces zones calculs En d autres termes r 'é Si c el é actives dont il d pend change ) une variable de Session . é cut ' à chaque fois qu une des é Si vous avez une source de donn é et que vous aimeriez r es è agir de mani re . vous aurez besoin de mettre en place un calcul ' ' é Notez qu habituellement vous n avez pas besoin de faire ceci parce que Meteor donne d ' chaque template et helper qu il affiche son propre calcul û s r que vos templates seront r é ( ce qui signifie que vous pouvez é actifs à afficher leurs donn ). es sources jà à ê tre Chaque source de donn é é es r les laisser savoir quand sa propre valeur change invalidate() sur le calcul , invalidation font é ' . Pour ce faire n éé ralement mis en place pour r , il appelle la fonction valuer simplement leurs contenus sur et c est ce qui arrive aux calculs de template ' ' . é é Les calculs sont g ' active surveille tous les calculs qui l utilisent pour qu elle puisse ( bien que les calculs de template ). galement la magie d essayer et redessiner la page plus efficacement pouvons avoir plus de contr ' ô Bien que nous , le sur ce que fait le calcul sur invalidation si vous en avez besoin . en pratique c est presque toujours le comportement que vous utiliserez Mettre en place un Calcul é Maintenant que nous comprenons la th beaucoup plus sens é. è orie derri re les calculs é actif en mettre un en place semble Tracker.autorun Nous pouvons utiliser la fonction bloc de code dans un calcul et le rendre r , pour enfermer un : Meteor.startup(function() { Tracker.autorun(function() { console.log('There are ' + Posts.find().count() + ' posts'); }); }); ' Tracker Notez que nous avons besoin d envelopper le bloc Meteor.startup() de charger la collection è En arri - ' ' Posts . , autorun re plan que la source de donn é cr é , e ensuite un calcul é es dont il d pend change è tr . é éé é est une source de donn es r ' é ' rieur d un bloc cutera seulement une fois que Meteor a fini é et le d éé clenche pour r valuer à chaque fois Nous avons mis en place un calcul vraiment ' s simple qui journalise simplement le nombre d articles à la console Posts.find() r é pour nous assurer qu il s ex ' à l int active , . Maintenant que il prendra soin de dire au calcul de valuer à chaque fois que le nombre d articles change . > Posts.insert({title: 'New Post'}); There are 4 posts. é Le r é r sultat net de tout ceci est que nous pouvons active de façon tr è , s naturelle é é prendra soin de le r ex ' é é crire du code qui utilise une donn è en sachant qu en arri . cuter juste au bon moment - è re plan le syst é me de d e pendance Créer des Posts Nous avons vu combien il à la base de donn ' é 7 é é tait facile de cr , , ' avec l appel Posts.insert . Mais nous ne pouvons pas attendre de nos utilisateurs es qu ils ouvrent la console pour cr À un moment er des posts en passant par la console nous devrons cr é . er un nouveau post é er une interface pour permettre à nos utilisateurs de poster de . nouvelles histoires sur notre application Construire la page d'ajout de post é Nous commençons par d finir une route pour notre nouvelle page : Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return Meteor.subscribe('posts'); } }); Router.route('/', {name: 'postsList'}); Router.route('/posts/:_id', { name: 'postPage', data: function() { return Posts.findOne(this.params._id); } }); Router.route('/submit', {name: 'postSubmit'}); Router.onBeforeAction('dataNotFound', {only: 'postPage'}); / . lib router js Ajouter un lien à l'en-tête é Une fois cette route d -ê dans notre en t te : , finie ' nous pouvons maintenant ajouter un lien à notre page d envoi <template name="header"> <nav class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggl e="collapse" data-target="#navigation"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="{{pathFor 'postsList'}}">Microscop e</a> </div> <div class="collapse navbar-collapse" id="navigation"> <ul class="nav navbar-nav"> <li><a href="{{pathFor 'postSubmit'}}">Créer un post</a></li > </ul> <ul class="nav navbar-nav navbar-right"> {{> loginButtons}} </ul> </div> </nav> </template> / / / . client templates includes header html ' Configurer notre route signifie que si un utilisateur se rend à l URL le template postSubmit . Écrivons donc ce template : /submit , Meteor affichera <template name="postSubmit"> <form class="main form page"> <div class="form-group"> <label class="control-label" for="url">URL</label> <div class="controls"> <input name="url" id="url" type="text" value="" placeholder="V otre URL" class="form-control"/> </div> </div> <div class="form-group"> <label class="control-label" for="title">Title</label> <div class="controls"> <input name="title" id="title" type="text" value="" placeholde r="Nommez votre article" class="form-control"/> </div> </div> <input type="submit" value="Submit" class="btn btn-primary"/> </form> </template> / / / _ client templates posts post Note : , ça fait beaucoup de code . Bootstrap Bien que seuls les é suppl . submit html mais ça vient simplement du fait que nous utilisons Twitter éé l , ments de formulaire soient essentiels . mentaires aideront à rendre notre appli un peu plus jolie ressembler à ça : les balises Cela devrait maintenant Le formulaire de soumission d'article ' ' . C est un formulaire simple puisque nous allons intercepter les en passant par JavaScript . è é ' ter de lui ajouter un champ é é v ‘ ’, action é nements sur le formulaire et mettre à jour les donn es Il n y aurait pas de sens à fournir une solution alternative sans JavaScript quand on consid est d é Nous n avons pas à nous inqui ' re qu une appli Meteor ne fonctionne plus du tout si JavaScript é. sactiv Créer des posts 'é é submit ( ô ' Lions un gestionnaire d ' 'é é d utiliser l v nement 'é é v nement à l plut v nement t qu un submit é é v nement du formulaire click . Il est pr éé f rable ), sur le bouton par exemple ' car cela permettra de prendre en compte toutes les façons possibles d envoyer le formulaire ( comme appuyer sur entr é e par exemple ). 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() }; } }); / post._id = Posts.insert(post); Router.go('postPage', post); / / _ client templates posts post . submit js ' Commit 7-1 - Avec une page d ajout de post et un lien vers celle ci da Voir sur GitHub ' Lancer l instance … é Cette fonction utilise jQuery pour v rifier les valeurs de nos divers champs de formulaire et construire un nouvel objet post à partir des r ' preventDefault sur l argument ' event é . sultats de notre gestionnaire pour ' ê , utilis é e sur une collection é . dans la base de donn , correspondante , Ainsi ' es ), . . é ment dirig é éé g n r ' La fonction 'ê pour l objet qui vient d id du Router va utiliser cet et nous amener sur la bonne page , é ’ id renvoie l go() La fonction un nouveau post est cr éé tre ins ' r pour construire l URL . éé, l utilisateur envoie le formulaire instantan r que le navigateur nous pouvons router l utilisateur vers la page de son nouveau post ( insert() ' û tre s n essaie pas de prendre les devants en essayant d envoyer le formulaire Finalement ' Nous devons nous assurer d utiliser ' et l utilisateur est . vers la page de discussion de ce nouveau post Ajouter un peu de sécurité é Cr er des posts est tr ils doivent ê è s bien tre authentifi ' , ' mais nous ne voulons pas laisser n importe quel visiteur le faire é . s pour pouvoir poster û, Bien s r nous pouvons commencer en é. cachant la page d ajout de post pour les utilisateurs non authentifi pourrait cr é : ê er un post depuis la console du navigateur sans s Cependant un utilisateur é, tre authentifi et nous ne . pouvons pas nous le permettre é Heureusement la s é d é sactiv é e par d curit é des donn é . es est incluse dans les collections Meteor faut lorsque vous cr é . ez un nouveau projet Elle est Cela vous permet de commencer é facilement à cr . er votre application tout en laissant les trucs ennuyeux pour plus tard ' , Notre appli n a plus besoin de ces petites roues insecure : - ! alors enlevons les Supprimons le paquet meteor remove insecure Terminal è Apr s avoir fait ça , . vous remarquerez que le formulaire de post ne fonctionne plus ' C est parce que sans le paquet insecure , les insert() côté client sur la collection des posts ne sont plus é. autoris s Nous devons soit donner quelques r é autoris é à ins rer un post , è é gles explicites à Meteor pour d ôé soit faire nos insertions c t finir quand un client est . serveur Autoriser les insertions de post , Pour commencer que notre formulaire fonctionne à nouveau é technique diff ôé nous allons voir comment autoriser les insertions de post c rente , , mais pour le moment fonctionnent à nouveau ' . Il s av è t client pour re que nous finirons par adopter une le simple code suivant permettra que les choses : Posts = new Meteor.Collection('posts'); Posts.allow({ insert: function(userId, doc) { // autoriser les posts seulement si l'utilisateur est authentifié return !! userId; } }); / . collections posts js Commit 7-2 Sans Insecure et avec des r Voir sur GitHub é dans lesquelles les clients sont autoris , Le nous disons userId gles de modifications sur les ' “ é é de l utilisateur proc ' “ ceci est un ensemble de circonstances s à faire des choses à la collection les clients peuvent ins … Lancer l instance Posts.allow , qui dit à Meteor que Nous appelons cas è ' rer des posts du moment qu ils ont un dant à la modification est pass é ” Post . Dans notre ” userId . lors des appels à allow et ( deny ou renvoie ' null ' é Et comme les comptes utilisateurs sont li userId pour é Nous avons r ê . ce qui est presque toujours utile s au noyau de Meteor , nous pouvons compter sur . tre toujours fiable ' ussi à nous assurer que l on doit é vous d ) é, si l utilisateur n est pas authentifi é connecter et de cr er un post ; ê é tre authentifi pour cr é . er un post vous devriez voir ceci dans votre console Essayez de : Échec de l'insertion : Accès refusé , Cependant è nous avons encore à nous occuper de quelques probl é Les utilisateurs non authentifi mes : é s peuvent toujours acc é der au formulaire de cr ation de . post ' é Le post n est pas li ôé c t ' ' à l utilisateur de quelque façon que ce soit Plusieurs posts peuvent é R ). serveur pour s occuper de ça è solvons ces probl ê éé tre cr ê s pointant vers la m . mes Sécuriser l'accès au nouveau formulaire . me URL ( ' et il n y a pas de code Commençons par ' é è viter l acc é s des utilisateurs non authentifi Nous le ferons au niveau du routeur Un hook ' (“ crochet ” ) en français . l action prise par le routeur , é en d é accessDenied ' ). d autre à faire . intercepte le processus de routage et change potentiellement ' Vous pouvez l imaginer comme un garde de s ( ' ' é, postSubmit attendu . : ( ) é , puis nous stoppons le routeur ici Modifions donc router js Router.route('/posts/:_id', { name: 'postPage', data: function() { return Posts.findOne(this.params._id); } }); Router.route('/submit', {name: 'postSubmit'}); var requireLogin = function() { if (! Meteor.user()) { this.render('accessDenied'); } else { this.next(); } } Router.onBeforeAction('dataNotFound', {only: 'postPage'}); Router.onBeforeAction(requireLogin, {only: 'postSubmit'}); . lib router js é ons aussi le template pour la page ' è acc s refus ’ é rifie et sinon afficher le template Router.route('/', {name: 'postsList'}); Nous cr qui v s Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return Meteor.subscribe('posts'); } }); / é curit è . ou de vous refuser l acc rifier si l utilisateur est authentifi au lieu du . finissant un route hook vos identifiants avant de vous laisser entrer Nous avons besoin de v ' s au formulaire d ajout de post é , accessDenied : ' il n a rien <template name="accessDenied"> <div class="access-denied page jumbotron"> <h2>Accès refusé</h2> <p>Vous ne pouvez pas accéder à cette page ! Veuillez vous connecter .</p> </div> </template> / / / _ client templates includes access . denied html ' è Acc Commit 7-3 é s à la page d ajout de post refus ' Voir sur GitHub Maintenant voir ceci , si vous allez à http // : : Lancer l instance / :3000 localhost … lorsque non authen / submit sans ê é, tre authentifi vous devriez Le template d'accès refusé , Ce qui est pratique à propos des hooks de routage ' ' é c est qu ils sont eux aussi r . actifs ' -- C est à ' ' ' dire que nous n avons pas besoin de penser à des callbacks quand l utilisateur s authentifie 'é quand l ' ' tat d authentification de l utilisateur change é change instantan ' é d explicite pour le g - , Authentifiez vous accessDenied ' accessDenied ment de rer ( à ' , postSubmit é î . puis essayer de rafra chir la page s afficher bri ) Pour existe r é é è me ( é authentifi è Apr , s tout é é crire -ê ' tre parfois le template è é é rifi . ' si l utilisateur actuel s que possible ( é stock , ê avant m é dans la m . La raison ' me qu il ait moire locale du ' qui est courant et que vous rencontrerez d autant plus que vous allez é vous pencher sur les subtilit afficher un ). vement avant que la page d ajout de post apparaisse ellement viter ce probl sans que nous ayons rien à Vous verrez peut è avec le serveur est v navigateur par le routeur et cela fonctionne d ailleurs pour tous les onglets en est que Meteor commence à afficher les templates d convers é le template utilis : s de latence entre client et serveur ), nous allons simplement ' cran de chargement pendant que nous attendons de savoir si l utilisateur est . ou pas ' à cet instant nous ne savons pas si l utilisateur a les identifiants corrects ne pouvons afficher aucun des templates accessDenied ou postSubmit , et nous Nous modifions donc notre hook pour utiliser notre template de chargement pendant que Meteor.loggingIn() est vrai : //... var requireLogin = function() { if (! Meteor.user()) { if (Meteor.loggingIn()) { this.render(this.loadingTemplate); } else { this.render('accessDenied'); } } else { this.next(); } } Router.onBeforeAction('dataNotFound', {only: 'postPage'}); Router.onBeforeAction(requireLogin, {only: 'postSubmit'}); . avant de le savoir / . lib router js Afficher un Commit 7-4 é ' . cran de chargement pendant l authentification ' Voir sur GitHub Lancer l instance Cacher le lien 'é La façon la plus simple d ' ' ' viter aux utilisateurs d essayer d atteindre cette page par erreur lorsqu ils ne sont pas connect é . s est de leur cacher le lien Nous pouvons faire cela facilement //... <ul class="nav navbar-nav"> {{#if currentUser}}<li><a href="{{pathFor 'postSubmit'}}">Créer un post< /a></li>{{/if}} </ul> //... / / / . client templates includes header html ' Commit 7-5 é. Afficher le lien d ajout de post seulement si authentifi Voir sur GitHub Le helper currentUser est fourni par le paquet ' ' Lancer l instance accounts 'é et est l quivalent Spacebars de Meteor.user() . Puisqu il est réactif, le lien apparaîtra ou disparaîtra selon que vous vous é connectez ou d ' . connectez de l application : Meteor Method : une meilleure abstraction et sécurité Nous avons r é, authentifi s é é ussi à s ' é et ' ' è curiser l acc s à la page d ajout de post pour les utilisateurs non viter qu ils puissent cr é ê er des posts m Nous avons cependant encore plusieurs choses à faire ' . me s ils trichent en utilisant la console : Marquer la date du post avec un timestamp ' ê S assurer que la m Ajouter des d me URL ne peut pas ê é tre post ' é ( tails à propos de l auteur du post ' ' e plus d une fois , ID nom d utilisateur , ) . etc Vous pourriez penser que nous pouvons faire tout cela dans notre gestionnaire de l submit . Cependant, en réalité, nous arriverions vite à plusieurs problèmes : 'é é v nement , Pour le timestamp ' , l utilisateur Le c ôé t é nous aurions à esp ' ' rer que l heure soit correcte sur l ordinateur de ce qui ne sera pas toujours le cas é client ne sera pas au courant de toutes les URL post ' seulement les posts que l utilisateur peut actuellement voir ). ' comment ceci fonctionne exactement URL du c ôé t client . , ê Enfin m es sur le site ( nous verrons plus tard ' é , par des gens utilisant la console , Pour toutes ces raisons ôé tails utilisateurs c t é , client des nous ne nous . 'é é il vaut mieux garder nos gestionnaires d v nements simples , é ' ce qui pourrait exposer notre appli à une exploitation nous faisons plus que les insertions et mises à jour les plus basiques M î Il conna tra Il n y a donc pas de moyen d assurer l unicit me si nous pourrions ajouter les d assurerions pas de leur exactitude . . Et si ô utilisons plut t une . thode é Une M ôé thode Meteor est une fonction c sont pas totalement remove de Revenons à é è trang Collection res – t , en fait é serveur qui est appel , en coulisses é sont toutes des M . thodes ôé e c les fonctions t . client insert , update , et é Voyons comment cr ' Elles ne nous ô er la n . tre post_submit.js . Plutôt que d insérer directement dans la collection Posts , nous allons appeler une M é é postInsert : thode nomm e 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() }; Meteor.call('postInsert', post, function(error, result) { // affiche l'erreur à l'utilisateur et s'interrompt if (error) return alert(error.reason); }); } }); Router.go('postPage', {_id: result._id}); / / / _ client templates posts post Meteor.call La fonction . submit js appelle une Methode nomm ' pouvez fournir des arguments pour l appel , finalement y attacher un callback ( ,' ici l objet é qui sera execut é . e par son premier argument post Vous ), construit depuis le formulaire ôé quand la Methode c t et serveur sera é. termin Les callbacks des m é une raison ou une autre return ' , error thodes meteor ont toujours deux arguments , ' l argument error pour interrompre le callback ). , existe ' et result . Si pour nous alerterons l utilisateur , Si tout fonctionne normalement î l utilisateur vers la page de discussion du post fra chement cr ( en utilisant nous redirigerons éé . e Test de sécurité é Nous allons profiter de cette opportunit utilisant le paquet é pour s curiser notre m é thode un peu plus en audit-argument-checks . ' é Ce paquet vous permet de tester n importe quel objet JavaScript selon un sch , Dans notre cas é authentifi ( ' ' nous l utiliserons pour tester que l utilisateur qui utilise la m en nous assurant que Meteor.userId() î est une cha ne é é é ma pr d . fini thode est bien ( String ), ' et que l objet postAttributes é pass ' é en tant qu argument à la m î thode contient les cha nes title et url , pour ne pas nous retrouver à entrer des morceaux aléatoires de données dans notre é . base de donn es é D finissons donc la m ’ é é : _id soumis postInsert de dans notre fichier posts.js t s l é tendre et le nom ( submitted ), ( ' final au client puisque de toute façon les m ( extend ) ' postAttributes ( username ) ' , ' ' é , ' ' de l utilisateur avant d ins en d autres mots . l objet thodes Meteor les Meteor.methods({ postInsert: function(postAttributes) { check(Meteor.userId(), String); check(postAttributes, { title: String, url: String }); var user = Meteor.user(); var post = _.extend(postAttributes, { userId: user._id, author: user.username, submitted: new Date() }); var postId = Posts.insert(post); } return { _id: postId }; / . lib collections posts js ainsi que l horodatage du post é é celui à l origine de l appel de cette m Posts = new Mongo.Collection('posts'); / é avec trois nouvelles rer le tout dans notre base de donn JavaScript }); collections/posts.js . Nous . Nous allons ensuite propri thode allow() supprimerons le bloc ignorent é ’ _id e et de retourner l thode ) dans un objet é Notez que la m _.extend() thode permet simplement d ’“é ” tendre , fait partie de la librairie Underscore éé un objet avec les propri t ' s d un autre . Commit 7-6 é En utilisant une m ' et qu elle vous . thode pour soumettre le post Voir sur GitHub ' Lancer l instance Bye Bye Allow/Deny é Les m . confiance / é thodes Meteor sont ex ' cut es sur le serveur , et donc Meteor leur fait ' é C est pourquoi les m . é thodes Meteor ignorent n importe quel callback allow deny é Si vous voulez ex , sur le serveur cuter du code avant chaque é nous vous sugg rons de jeter un insert , update , ou remove ê - m œ me . il au paquet collection hooks Éviter les doublons ' Nous ferons un dernier test avant d int é é éé t cr , auparavant ' ' é é grer notre m thode . Si un post avec la m , nous n ajouterons pas le lien une nouvelle fois allons rediriger l utilisateur vers ce post . ê é me url a d jà mais au lieu de cela nous Meteor.methods({ postInsert: function(postAttributes) { check(this.userId, String); check(postAttributes, { title: String, url: String }); var postWithSameLink = Posts.findOne({url: postAttributes.url}); if (postWithSameLink) { return { postExists: true, _id: postWithSameLink._id } } var user = Meteor.user(); var post = _.extend(postAttributes, { userId: user._id, author: user.username, submitted: new Date() }); var postId = Posts.insert(post); }); return { _id: postId }; } / / . lib collections posts js é Nous cherchons dans notre base de donn , un nous retournons ( return ) ’ _id l es un post avec la m ex é é cuter la d é clenchons un appel à me url 'é é v pour è . re ' return , la méthode s interrompt à ce moment sans insert , évitant ainsi avec élégance de créer des doublons. claration ôé nement c Si nous en trouvons t ' Tout ce qui nous reste à faire est d utiliser cette nouvelle information helper d . é é postExists:true de ce posts avec la propri informer le client de ce cette situation particuli Et puisque nous d ê t ' postExists client pour afficher un message d avertissement : dans notre 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() }; Meteor.call('postInsert', post, function(error, result) { // affiche l'erreur à l'utilisateur et s'interrompt if (error) return alert(error.reason); // affiche ce résultat mais route tout de même if (result.postExists) alert('Ce lien a déjà été utilisé'); }); Router.go('postPage', {_id: result._id}); } }); / / / _ client templates posts post . submit js Commit 7-7 ' ' é S assurer de l unicit Voir sur GitHub ' . de l url du post ' Lancer l instance Trier les posts é Maintenant que nous avons une date de cr ' ' é s assurer qu ils soient class ' é utiliser l op rateur , ation sur tous nos posts sort . s en fonction de cet attribut , de Mongo et un signe indiquant si le tri doit ê Pour cela é qui attend un objet constitu é tre croissant ou d . croissant , il semble bienvenu de nous pouvons simplement é des cl s par lesquelles trier Template.postsList.helpers({ posts: function() { return Posts.find({}, {sort: {submitted: -1}}); } }); / / / _ client templates posts posts . list js Commit 7-8 é Tri des posts par date de cr ' Voir sur GitHub é Ça a demand ' , un peu de travail Lancer l instance mais nous avons maintenant une interface utilisateur qui é permet d ajouter du contenu de façon s é curis e à notre appli é é de le modifier ou de le supprimer . ! Mais toute application qui permet à ses utilisateurs de cr la possibilit . ation er du contenu doit aussi leur donner Ce sera le sujet du prochain chapitre . La Compensation de Latence , Dans le dernier chapitre é M nous avons pr é é sent 7.5 SIDEBAR un nouveau concept du monde Meteor : les . thodes Sans compensation de latence é Une M ' thode Meteor est un moyen d ex é . façon structur e , Dans notre exemple é é cuter une s é nous avons utilis é assurer que les nouveaux articles seraient tagu ' l heure du serveur , Cependant è probl . é valeurs al é si Meteor ex Consid é une M thode car nous voulions nous ' s avec le nom et l id de leur auteur ainsi que . é me ' rie de commandes sur le serveur d une , cute des Methodes dans sa plus simple façon é rez la s ’é é quence suivante d ' v nements atoires choisies seulement pour l exemple ): ( note : nous aurions un les horodatages sont des +0 : ' +200 : +500 : ms L utilisateur clique sur un bouton et le navigateur renvoie un appel de M é ms Le serveur fait des changements dans la base de donn ms Le client reçoit ces changements é Si Meteor op ' , rait de cette façon é actions et l affichage des r é proximit moderne ). avec le serveur ! , ' é sultats ( é ce d calage calage entre la r é . alisation de ces é tant plus ou moins visible selon votre Nous ne pouvons nous le permettre dans une application web Compensation de latence . thode es Mongo met à jour l interface utilisateur il y aurait alors un petit d é Avec compensation de latence Pour é è viter ce probl é Quand nous avons d é dans le r – pertoire ' , me é finit notre M ' ' ' ' ! collections/ . Ceci signifie qu elle est disponible pour le serveur cutera sur les deux en m Quand nous faisons un appel de M é ê me temps thode . Compensation de latence post , nous l avons placée à l intérieur d un fichier thode é et elle s ex é Meteor introduit un concept appel , et le client ' le client envoie l appel au serveur , mais simule é galement simultan workflow devient +0 +0 : ms : ms ' é é ment l action de la M : ' ôé thode sur ses collections c ' ' é Le client simule l action de l appel de M ' é Notre ms ms : : ôé thode sur les collections c t é é ' é ( é é qui sont g n . ê ralement les m , é, s en les mes . ). ter ceci é sultat pour l utilisateur est de voir les changements instantan serveur revient un peu plus tard client et es Mongo Le client reçoit ces changements et annule ses changements simul ' . thode . Le serveur fait les changements dans la base de donn L interface utilisateur change pour refl é ter ceci remplaçant par les changements du serveur Le r client L utilisateur clique sur un bouton et le navigateur renvoie un appel de M met à jour l interface utilisateur pour refl +200 +500 . t il peut y avoir ( ) ou non . que les documents canoniques du serveur arrivent . ment Quand la r é ponse du des changements visibles à mesure Une chose à retenir est donc que nous devons nous assurer que nous simulons les documents aussi proches de la r é é alit que . possible Observer une compensation de latence ' Nous pouvons faire un petit changement à l appel de la m . action , Pour ce faire ' thode nous utiliserons la fonction bien pratique retarder l appel de la m . é é thode de 5 , secondes mais (' post pour voir cela en Meteor._sleepForMs() c est un point crucial ) pour seulement sur le serveur Nous utiliserons sur le client é M ( isServer comme une thode que Meteor ex ' “é ”) bauche é . ou sur le serveur cute sur le client en parall é cours d ex é pour demander à Meteor si la M è , le Une é , caract pendant que la . ' caract re è re “ vraie ” é M thode est en cution sur le serveur nous allons retarder l avancement des choses de è ' e bauche est la simulation d une ' é Nous allons donc demander à Meteor si le code est en cours d ex le cas é thode est actuellement invoqu (server) à la fin du titre de notre article (client) : . 5 cution sur le serveur . î secondes et ajouter la cha ne de , Sinon î ' Si c est nous ajouterons la cha ne de Posts = new Mongo.Collection('posts'); Meteor.methods({ postInsert: function(postAttributes) { check(this.userId, String); check(postAttributes, { title: String, url: String }); if (Meteor.isServer) { postAttributes.title += postAttributes.title + "(server)"; // attente de 5 secondes Meteor._sleepForMs(5000); } else { postAttributes.title += "(client)"; } var postWithSameLink = Posts.findOne({url: postAttributes.url}); if (postWithSameLink) { return { postExists: true, _id: postWithSameLink._id } } var user = Meteor.user(); var post = _.extend(postAttributes, { userId: user._id, author: user.username, submitted: new Date() }); var postId = Posts.insert(post); }); return { _id: postId }; } / . collections posts js Si nous nous arr ê , tions ici é la d monstration ne serait pas concluante ' . on dirait que le formulaire de soumission d article se met en pause pendant , de rediriger vers la liste des posts principaux ' 'é Dans l 5 , tat des choses secondes avant et pas grand chose d autre se passe . , Pour comprendre cela revenons au manager de l 'é é v nement de soumission de post : 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() }; Meteor.call('postInsert', post, function(error, result) { // affiche l'erreur à l'utilisateur et s'interrompt if (error) return alert(error.reason); // affiche ce résultat mais 'route' tout de même if (result.postExists) alert('This link has already been posted'); }); Router.go('postPage', {_id: result._id}); }); } / / / _ client templates posts post é Nous avons plac é la m . thode rediriger ' . submit js notre appel de routeur ' Router.go() é à l int ' rieur du callback de l appel de Ce qui signifie que le formulaire attend que cette m é thode r é ussisse avant de . C est normalement la bonne mani ' è . re de faire les choses Apr è , s tout vous ne pouvez pas , rediriger l utilisateur avant de savoir si la soumission de son post est valide ou pas ê ce serait extr é mement d , une nouvelle fois è , en arri 'ê routant d re é tre redirig è une premi 'ê re fois et ensuite d parce que é tre redirig é , vers la page de soumission de post pour corriger vos donn es . tout cela en quelques secondes , Mais pour le bien de cet exemple é imm diatement postsList ( . Ainsi , nous voulons voir les r ' é sultats de nos actions nous allons changer l appel du routeur pour rediriger vers la route nous ne pouvons pas diriger vers le post car nous ne connaissons pas son _id ), é en dehors de la m ' thode , pour ensuite l extraire du callback et voir ce qui se passe : Template.postSubmit.events({ 'submit form': function(event) { event.preventDefault(); var post = { url: $(e.target).find('[name=url]').val(), title: $(e.target).find('[name=title]').val() }; Meteor.call('postInsert', post, function(error, result) { // affiche l'erreur à l'utilisateur et s'interrompt if (error) return alert(error.reason); // affiche se résultat mais 'route' tout de même if (result.postExists) alert('This link has already been posted'); }); Router.go('postsList'); } }); / / / _ client templates posts post D . submit js ' Commit 7-5-1 é ' Voir sur GitHub é Si nous cr . action ons un article maintenant è Premi dans la liste , rement é reli … monstration de l ordre d apparition des articles en uti , , nous verrons clairement la compensation de latence en éé un article est ins ): à Github ' Lancer l instance r avec (client) dans le titre ( le premier article Notre article comme initialement stocké dans la collection côté client , Ensuite éé ins r , cinq secondes plus tard par le serveur é il est proprement remplac avec le vrai document qui a : Notre article une fois que le client reçoit la mise à jour de la collection côté serveur Méthodes des collections sur le client éé t é Vous pourriez penser que les M ê ô tre plut . t simples , En fait mutation de collection é Quand vous d é train de d , mots é thodes sont compliqu é nous avons d , insert , update é jà vu trois m et é thodes quand vous appelez s cela è thodes tr é 'posts' , e : posts/insert , posts/update Posts.insert() , mais en fait elles peuvent s simples vous : é les M thodes de et ê tes implicitement en , thode de compensation de latence qui fait deux choses ' posts/delete . En d autres sur votre collection client é M è remove . finissez une collection serveur appel finir trois M es apr vous appelez une : 1. é Des v deny rifications pour voir si vous faites la mutation en appelant des callbacks 2. ( ' cependant il n est pas n ). é allow et cessaire que cela arrive dans la simulation é La modification vers le stockage de donn - . es sous jacent Des méthodes qui appellent des Méthodes Si vous suivez bien é une autre M , thode -ê ( posts/insert ) vous venez peut Quand la simulation é tre juste de r é quand nous ins ( ôé version c ' effectuons la simulation d un t insert ( ' t serveur ) thode . ' post appelle Comment ça marche é est en cours d ex , cution nous rons donc dans notre collection cliente , ? ôé nous attendons que la version c t ), mais serveur . post le fasse é Par cons ' c é rons notre article thode é nous ins , insert ô é quent , quand la M é s inqui é client de la M nous ne faisons pas le vrai de aliser que notre M ter de la simulation N oubliez pas comme pr é , post côté serveur appelle insert thode ' et l insertion continue sans encombre éé c . ' il n y a pas besoin de é demment de supprimer les changements effectu chapitre avant de passer au suivant . s au cours de ce Editer des Posts 8 é Maintenant que nous pouvons cr . et les supprimer ' la prochaine Puisque l interface est simple à mettre en è voir comment Meteor g ' , er des posts é tape est de pouvoir les modifier œ uvre . , ' é c est le moment id al de re les permissions des utilisateurs . Voyons d abord le router ' é Nous lui ajoutons une route afin d acc é : des posts et lui donnons des donn es der à la page de modification Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return Meteor.subscribe('posts'); } }); Router.route('/', {name: 'postsList'}); Router.route('/posts/:_id', { name: 'postPage', data: function() { return Posts.findOne(this.params._id); } }); Router.route('/posts/:_id/edit', { name: 'postEdit', data: function() { return Posts.findOne(this.params._id); } }); Router.route('/submit', {name: 'postSubmit'}); var requireLogin = function() { if (! Meteor.user()) { if (Meteor.loggingIn()) { this.render(this.loadingTemplate); } else { this.render('accessDenied'); } } else { this.next(); } } Router.onBeforeAction('dataNotFound', {only: 'postPage'}); Router.onBeforeAction(requireLogin, {only: 'postSubmit'}); / . lib router js La template de modification des posts Concentrons nous maintenant sur la template . Notre template postEdit reste tr è s classique : <template name="postEdit"> <form class="main form page"> <div class="form-group"> <label class="control-label" for="url">URL</label> <div class="controls"> <input name="url" id="url" type="text" value="{{url}}" placeholder ="Votre URL" class="form-control"/> </div> </div> <div class="form-group"> <label class="control-label" for="title">Title</label> <div class="controls"> <input name="title" id="title" type="text" value="{{title}}" placeho lder="Nommez votre post" class="form-control"/> </div> </div> <input type="submit" value="Submit" class="btn btn-primary submit"/> <hr/> <a class="btn btn-danger delete" href="#">Supprimer le post</a> </form> </template> / / / _ client templates posts post Et voilà le fichier . edit html post_edit.js qui va avec : Template.postEdit.events({ 'submit form': function(e) { e.preventDefault(); var currentPostId = this._id; var postProperties = { url: $(e.target).find('[name=url]').val(), title: $(e.target).find('[name=title]').val() } Posts.update(currentPostId, {$set: postProperties}, function(error) { if (error) { // affiche l'erreur à l'utilisateur alert(error.reason); } else { Router.go('postPage', {_id: currentPostId}); } }); }, 'click .delete': function(e) { e.preventDefault(); if (confirm("Delete this post?")) { var currentPostId = this._id; Posts.remove(currentPostId); Router.go('postsList'); } } }); / / / _ client templates posts post . edit js Normalement la majeure partie de ce code devrait vous Deux é é v nements sont pr ' é 'é é formulaire et l autre pour l 'é é L v v nement è nement de suppression est tr é d sents dans notre template click s simple ' : ' . é . sent 'é é l un pour l v submit nement du . du lien de suppression du post ê on emp post actuel depuis les informations de la template ' tre familier à pr ' : faut et on demande une confirmation à l utilisateur l utilisateur sur l accueil ê , ' che l activation des . ' é é v , Enfin si on l obtient nements par on r é è cup on supprime le post et on redirige ' re l ID du 'é é L v , nement de mise à jour du post est un peu plus long , avoir , une fois de plus du formulaire ) et r é ê é emp cup éé r ' ch ' l activation des é, l ID du post concern ' $set é ( é nements classiques on r é è cup ( è s lors de la soumission re les valeurs du formulaire depuis Collection.update() ' ) ' é de Meteor avec l op rateur et utilisons un callback pour afficher une erreur é chec ou renvoie l utilisateur sur le post concern è. succ thode qui remplace un ensemble de champs si la mise à jour est un v Apr postProperties . la page et on les sauvegarde dans l objet Nous passons alors cet objet à la m é é é. mais pas plus compliqu si la mise à jour est un s Ajouter des liens Nous devons bien leurs posts é videmment rajouter un lien pour que les utilisateurs puissent modifier : <template name="postItem"> <div class="post"> <div class="post-content"> <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3> <p> submitted by {{author}} {{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}} </p> </div> <a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Discute r</a> </div> </template> / / / _ client templates posts post , De plus . item html ' . nous ne devons pas afficher ce lien à n importe qui rajoutons un helper ownPost : ' C est pour cela que nous Template.postItem.helpers({ ownPost: function() { return this.userId === Meteor.userId(); }, domain: function() { var a = document.createElement('a'); a.href = this.url; return a.hostname; } }); / / / _ client templates posts post . item js Formulaire d'édition Commit 8-1 'é Ajout des formulaire d Voir sur GitHub ' Lancer l instance , Notre formulaire pour modifier les posts parait correct . modifier tout de suite -- ? Que se passe t il . dition pourtant vous ne pourrez pas les Mettre en place les permissions é Depuis que nous avons supprim é provenant du client sont cat Pour r é fichier , gler cela insecure , toutes les requêtes de modifications é . goriquement refus es . nous devons fixer des permissions permissions.js dans le dossier é seront charg le paquet es en premier ( , é Pour commencer cr ez un nouveau lib . Nous serons ainsi sûr que nos permissions ): et disponible dans les deux environnements // check that the userId specified owns the documents ownsDocument = function(userId, doc) { return doc && doc.userId === userId; } / . lib permissions js é Dans le chapitre Cr , er des Posts é ins é rions les nouveaux posts via des m Mais maintenant que nous le fichier ' posts.js é nous n avions pas utilis ôé thodes c t la m serveur ( é thode allow() qui passent outre é , ditons et supprimons des posts depuis le client et rajoutons la fameuse m é thode car nous retournons dans allow() : Posts = new Mongo.Collection('posts'); Posts.allow({ update: function(userId, post) { return ownsDocument(userId, post); }, remove: function(userId, post) { return ownsDocument(userId, post); }, }); //... / / . lib collections posts js ) allow() . Commit 8-2 Ajout de permissions basiques pour v é é rifier le propri … tai ' Voir sur GitHub Lancer l instance Limiter les éditions ' Ce n est pas parce que vous pouvez 'é ' d diter toutes les propri éé. t ' ' s é ê diter vos propres posts que vous devez , Par exemple ' tre capable nous ne voulons pas que l utilisateur cr . é e un post et l assigne à quelqu un d autre Donc nous allons utiliser le callback certains champs deny() ' 'é pour permettre à l utilisateur d : diter seulement Posts = new Mongo.Collection('posts'); Posts.allow({ update: function(userId, post) { return ownsDocument(userId, post); }, remove: function(userId, post) { return ownsDocument(userId, post); }, }); Posts.deny({ update: function(userId, post, fieldNames) { // may only edit the following two fields: return (_.without(fieldNames, 'url', 'title').length > 0); } }); //... / / . lib collections posts js Commit 8-3 . Accepter le changement de seulement certain champs ' Voir sur GitHub fieldNames Nous transmettons le tableau champs qui ne sont pas , Si tout se passe bien url , la taille du tableau vaudra -ê ). é tre remarqu modification des posts la pr soumettre un lien et l è , posts title . 1 ê tre de ou plus , 0. ' Si quelqu un essaie de et le callback retournera true chera la mise à jour Vous aurez peut à ce probl s et en d Underscore nous obtenons un tableau qui contient les ou ê ce qui emp é qui contient la liste des champs modifi le tableau sera vide et sa taille devra jouer un peu avec le code ( ’ without() utilisant la fonction Lancer l instance 'é ' é que nous ne v é rifions nulle part dans notre code de é. sence de liens dupliqu ' s ' Cela veut dire qu un utilisateur pourrait diter afin de changer l URL pour passer outre la v é me serait d utiliser une m thode de Meteor é . rification ( Meteor.methods() ) La solution pour modifier les mais nous avons voulu vous montrer cela pour le principe et vous exercer . Les appels de méthode vs la manipulation de données ô é c é Pour cr , er des posts é nous avons utilis une m é thode Meteor allow transactions de donn ' et deny client postInsert , par update contre pour les modifier et les supprimer nous appelons directement depuis le client en utilisant t et remove é pour s curiser les é . es ' é Quand utiliser l une ou l autre m thode ? Lorsque les choses sont relativement simple et que vous pouvez rapidement adapter é votre s curit é avec allow et directement depuis le client Par contre ôé contr l , é Les m . ù à partir du moment o ' par l utilisateur ), utilisateur deny , il est plus simple de faire les opérations ( vous devez faire des choses qui ne doivent pas ' é thode Meteor Meteor.methods . é thodes Meteor sont aussi plus adapt s dans certains cas : î ô Quand vous devez conna tre ou renvoyer des valeurs via un callback plut ' é activit Pour les fonctions op é . é é rant de grosses manipulations sur la base de donn é Pour des calculs sur la base de donn œ t que et la synchronisation prennent effet seraient trop lourdes à transmettre entre le client et le serveur Jetez un tre comme dater un nouveau post ou l assigner au bon vous devriez utiliser une m d attendre que la r ê es ( exemple : , count é il à notre blog pour une exploration plus en d . average ). , sum . tail de ce sujet es qui Allow et Deny è Le syst é donn é me de s 8.5 SIDEBAR é ô curit de Meteor nous permet de contr é es sans avoir à d é finir des M Parce que nous avions besoin de r éé des propri é éé d jà t s suppl é , t post é . ' é mentaires et d é cider d une action sp thode post ' é aliser des tâches auxiliaires comme d é utiliser une M ' thodes à chaque fois que l on fait des changements é e ler les modifications de la base de é sp ' corer l article avec ' ciale quand l URL de l article éé é cifique quand un article est cr é tait tait tout à é. fait sens ' D un autre c ' ô é, t é nous n avions pas vraiment eu besoin de cr mettre à jour ou supprimer des articles avait la permission de r é , es é Nous avions juste besoin de v é aliser ces actions Utiliser ces callbacks nous laisse donn . , é et cela fut facilit ê é tre plus d è ' thodes pour rifier si l utilisateur par les callbacks allow deny . et claratif à propos des modifications de la base de et dire quels types de mises à jour peuvent avec le syst é er de nouvelles M ê é . tre utilis es ' ' è Le fait qu ils s int . grent me de comptes est un bonus Multiples callbacks é Nous pouvons d finir autant de callbacks ' ' ’ qu au moins l un d entre eux retourne Donc quand c ôé v t Posts.insert true é é que n dans un navigateur - insert ' , ' rifications allowed n en trouve aucune ê De la m me façon , callbacks retourne Nous avons juste besoin ' é ), ' ( ' , il n autorisera pas le insert nous pouvons d le serveur va faire à son tour toutes les ' . et retournera une erreur é finir un ou plusieurs callbacks 403 deny . Si true , le changement sera annulé et une erreur 403 aussi bien que chaque callback . au client un de ces é. sera retourn é seront ex é. cut s ' S il La insert réussi, un ou plusieurs callbacks allow deny insert . rer peu importe si c est depuis le code qu il peut jusqu à ce qu il en trouve une qui retourne true logique de tout cela signifie que pour un insert . cessaires durant le changement qui est en train de s op client de votre application ou depuis la console é ' est appel allow Note: n/e est l'abréviation de Not Executed ' , En d autres mots Meteor descend la liste de callback en partant du premier avec ' ' ' deny , puis allow , et exécute chaque callback jusqu à ce que l un d entre eux retourne true . avec ' ' article appartient à l utilisateur courant ' droits d administration . ' , é et un second qui v ' allow() , l un qui vérifie si un Un exemple pratique de ce pattern serait d avoir deux callbacks ' rifie si l utilisateur courant a les Si l utilisateur courant est un administrateur ' capable de mettre à jour n importe quel article . , ' ' , ' cela assure qu il sera puisqu au moins l un de ces callback retournera true Compensation de latence - é Souvenez vous que ces M é sont compens en latence é thodes de mutation de base de donn , comme toutes les autres m é . thodes es ( telles que .update() Donc par exemple , si vous essayez de supprimer un article qui ne vous appartient pas via la console du navigateur ' verrez l article bri puis r é è î ù vement dispara tre au moment o ' î , appara tre lorsque le serveur l informe que é. ) , vous , votre collection locale perd le document ' , non en fait le document n a pas éé t supprim ' û Bien s ( è apr r ce comportement n est pas un probl , s tout è é me quand il est d é clench é si les utilisateurs essaient de bidouiller avec les donn ' passe dans leur navigateur n est pas vraiment votre probl ' ' . assurer que cela n arrive pas dans l interface utilisateur è me ). depuis la console es dans la console , Cependant , Par exemple , ce qui se vous devez vous vous devez vous donner ' du mal pour vous assurer que vous ne montrez pas les boutons supprimer lorsqu ils ne sont é pas autoris . s à supprimer , Heureusement ( par exemple , puisque vous pouvez partager le code de permissions entre client et serveur vous pourriez é la mettre dans le r é code suppl é lib pertoire è crire une fonction biblioth ) é, partag . que canDeletePost(user, post) et cela ne requiert habituellement pas beaucoup de mentaire Permissions côté serveur - Souvenez vous que le syst é de donn è é es initi ' me de permissions s applique seulement sur les mutations de base es par le client . Sur le serveur , é Meteor assume que toutes les op rations sont . permises ù Ceci signifie que dans le cas o qui pourrait article é é v rifi . ê tre appel é vous é é cririez une M , e par le client ' ôé thode Meteor c t serveur deletePost n importe qui serait capable de supprimer chaque Vous ne voulez donc probablement pas faire cela à moins que vous ayez ' les permissions à l int é rieur de cette M é thode . é galement Erreurs 9 Utiliser simplement la boite de dialogue de navigateur standard ' l utilisateur quand il y a un probl ' ' è alert() pour avertir ' me avec l envoi de leur formulaire n est pas tr é et ce n est clairement pas fait pour une bonne exp rience utilisateur . è , s satisfaisant Nous pouvons faire . mieux , À la place ' é construisons un m ' ' canisme de rapport d erreurs plus versatile qui pr ' mieux l utilisateur de ce qu il se passe sans l interrompre é Nous allons impl menter un syst é sup rieur droit de la fen ê , tre è é viendra . me simple qui affiche les nouvelles erreurs dans le coin ' . semblable à l application populaire Mac OS Growl Présentation des Collections Locales , Pour commencer é nous devons cr . er une collection dans laquelle nous stockerons nos erreurs ' Sachant que les erreurs sont seulement pertinentes pour la session en cours et n ont besoin 'ê d , tre persistantes en aucun cas . collection locale , navigateur , nous allons faire quelque chose de nouveau Cela signifie que la collection nous cr ' ), é ons l erreur dans le dossier une collection cliente uniquement é puisque les donn é e c ôé t existera uniquement dans le . , donn er une et ne fera aucune tentative de synchronisation avec le serveur Pour accomplir cela ( Errors é et cr client ( pour faire de la collection avec son nom de collection MongoDB configur é es de cette collection ne seront jamais sauvegard serveur ): é à null es dans la base de // Collection Locale (client-seulement) Errors = new Meteor.Collection(null); / / . client helpers errors js Maintenant que la collection a é é éé , t cr e nous pouvons ajouter une fonction . nous appellerons pour y ajouter des erreurs ' throwError é Nous n avons pas besoin de nous pr que occuper de allow ' deny ou ' è ou d autre probl . me de s é é curit puisque cette collection est “ locale ” à l utilisateur en cours throwError = function(message) { Errors.insert({message: message}); }; / / . client helpers errors js ' ' L avantage d utiliser une collection locale pour stocker les erreurs est que , collections è mani elle est r é é re r active ê active de la m – , é me façon que nous affichons les donn ' es de n importe quelle autre . Afficher les erreurs Nous allons afficher les erreurs en haut de notre layout principal : <template name="layout"> <div class="container"> {{> header}} {{> errors}} <div id="main"> {{> yield}} </div> </div> </template> / / . client templates application layout html Maintenant , é cr ' cela veut dire que nous pouvons afficher les erreurs d une collection / comme toutes les ons les templates errors et error dans errors.html : <template name="errors"> <div class="errors row-fluid"> {{#each errors}} {{> error}} {{/each}} </div> </template> <template name="error"> <div class="alert alert-danger" role="alert"> <button type="button" class="close" data-dismiss="alert">×</button > {{message}} </div> </template> / / / . client templates includes errors html Templates jumeaux Vous noterez que nous mettons deux templates dans un seul fichier , maintenant , mais bien é nous avons essay , ( pour Meteor , é “ rer à la convention ' Jusqu à un fichier , un template main.html tr è s confus !). ' ô vu que les deux templates d erreur sont plut une exception et les mettre dans le m ê , t courts nous allons faire é me fichier pour rendre notre r pertoire un peu . plus clair ' Nous avons juste besoin d int é grer notre helper de template Template.errors.helpers({ errors: function() { return Errors.find(); } }); / / ”, mettre tous vos templates dans un seul fichier fonctionne aussi bien que cela rendrait Dans ce cas ' d adh . / . client templates includes errors js , et nous seront fin pr ê ts ! è Vous pouvez d s à pr ' é . sent tester nos nouveaux messages d erreur manuellement simplement la console de votre navigateur et tapez : throwError("I'm an error!"); Tester les messages d'erreur. Commit 9-1 ' . Rapport d erreur basique Voir sur GitHub ' Lancer l instance Ouvrez Deux Types d'Erreur , A ce stade ' il est important de faire la distinction entre les erreurs au niveau de l application (“ - app level ”) ' de celles au niveau du code Les erreurs au niveau de l application ' l utilisateur , ' , ainsi de suite ' é . ' - code level é é sont g n ”). é ralement d é clench es par Ces erreurs comprennent notamment les , les erreurs de permission ' “ les erreurs de type ” introuvables ' et Ce sont le genre d erreurs que l on veut montrer à l utilisateur pour gler tout probl è è é. me rencontr Les erreurs au niveau du code mani ) app level et l on peut agir sur celles ci erreurs de validation l aider à r ( -. (“ é re inattendue par de r - ( ), code level ô é, , de leur c t els bugs dans votre code é sont d , Dans ce chapitre es de et vous ne voulez , probablement pas les afficher à vos utilisateurs directement une trace avec un service tiers de suivi des erreurs é clench ( ). ' ô mais plut t d en garder tel que Kadira ' , nous nous concentrerons sur le premier type d erreur . pas sur le suivi des bugs Créer des erreurs é Nous savons d sormais comment afficher des erreurs avant de voir quoi que ce soit : erreur alert . En fait ' , é nous avons d . en place : postSubmit par la nouvelle fonction - é mais encore faut il en d jà impl notre avertissement lors d un article doublon dans , é é ment clencher une é un bon sc nario pour une Nous remplacerons simplement les throwError que nous venons de mettre 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() }; Meteor.call('postInsert', post, function(error, result) { // display the error to the user and abort if (error) return throwError(error.reason); // show this result but route anyway if (result.postExists) throwError('This link has already been posted'); } }); / Router.go('postPage', {_id: result._id}); }); / / _ client templates posts post Tant que nous y sommes , . submit js nous allons faire la m ê me chose pour postEdit : Template.postEdit.events({ 'submit form': function(e) { e.preventDefault(); var currentPostId = this._id; var postProperties = { url: $(e.target).find('[name=url]').val(), title: $(e.target).find('[name=title]').val() } Posts.update(currentPostId, {$set: postProperties}, function(error) { if (error) { // display the error to the user throwError(error.reason); } else { Router.go('postPage', {_id: currentPostId}); } }); }, //... }); / / / _ client templates posts post . edit js Commit 9-2 ' Utilisation du rapport d erreurs Voir sur GitHub ê Essayez par vous m me : tentez de cr é Comme cette adresse est d é ' Lancer l instance ' http://meteor.com . er un article et entrez l adresse é jà attach . ' , e à un article dans l installation vous devriez voir : Déclencher une erreur Effacer les Erreurs ' - ê Vous noterez que les messages d erreur disparaissent par eux m . secondes û Cela est en fait d é avons ajout é e au tout d : @keyframes fadeOut { 0% {opacity: 0;} 10% {opacity: 1;} 90% {opacity: 1;} 100% {opacity: 0;} } //... .alert { animation: fadeOut 2700ms ease-in 0s 1 forwards; //... } / / . s quelques à un peu de magie CSS incluse dans la feuille de style que nous but de ce livre client stylesheets style css è me apr é Nous d é opacit fadeOut finissons une animation CSS ( 0%, 10%, 90%, à ' % é L animation s ex ' 100 .alert . animation à la classe ' et de la dur 2700 cutera pendant qui pr é é é cise quatre images cl ' ) e totale de l animation , millisecondes au total éé pour la propri t et appliquons cette 'é utilisera l quation de timing ease-in , s exécutera avec un délai de 0 secondes, une seule fois, et finalement restera sur la è derni é re image cl ( ) keyframe é . une fois termin e Animations contre Animations Vous vous demandez peut é é ôé pr d contr termin l é -ê tre pourquoi nous utilisons des animations CSS es et en dehors du contr - es par Meteor lui m ê me ), ô le de notre application . ' ' Bien que Meteor offre une aide à l insertion d animations é ê chapitre soit focalis “ animations CSS Animations . , Cela fonctionne bien b ” tes . é qui sont au lieu d animations nous voulions que ce ' Nous utiliserons donc pour l instant des et garderons les choses plus sophistiqu mais si vous d ) trois fois par exemple sur les erreurs , ' ( clenchez plusieurs erreurs ' ' ( é es pour le chapitre en soumettant le m ê me lien vous remarquerez qu elles s empileront les unes au dessus des autres : Débordement de pile. éé Et cela parce que alors que les é toujours pr ' l . sents dans le DOM ments .alert , disparaissent visuellement é Nous devons r . gler cela . C est exactement dans ce genre de situation que Meteor brille é est r , active Puisque la collection é tout ce que nous devons faire pour nous d les supprimer de la collection ils sont en fait ! ` ’ Errors barrasser de ces vieilles erreurs est de Nous utiliserons fin du timeout ( Meteor.setTimeout , 3000 dans ce cas é pour sp cifier une fonction callback à ). millisecondes ê é tre ex é cut e à la Template.errors.helpers({ errors: function() { return Errors.find(); } }); Template.error.onRendered(function() { var error = this.data; Meteor.setTimeout(function () { Errors.remove(error._id); }, 3000); }); / / / . client templates includes errors js Commit 9-3 Effacer les erreurs apr Voir sur GitHub Le callback ' é À l int onRendered rieur du callback ' , this é nous permet d acc ). é est d é clench éè se r der aux donn f è 3 s . secondes ' Lancer l instance éé une fois notre template interpr ' t . dans le navigateur , re à l instance courante du template ' é ' é es de l objet en cours d interpr tation ( this.data et une erreur dans notre cas Mettre en place une validation ' , Jusqu ici , minimum . post ' nous n avons pas impos é une quelconque validation de notre formulaire Au nous voulons que les utilisateurs fournissent une URL et un titre pour leur nouveau - ' Assurons nous donc qu ils le font . é : Nous allons faire deux choses pour signaler les champs non renseign allons donner une classe sp é probl . é ciale CSS . matique du formulaire . dessous du champ Puis , has-error au div s ' è premi , rement nous parent de n importe quel champ ' nous allons afficher un message d erreur utile juste en Pour commencer helpers , é pr parons notre template postSubmit ' pour qu il accepte ces nouveaux : <template name="postSubmit"> <form class="main form page"> <div class="form-group {{errorClass 'url'}}"> <label class="control-label" for="url">URL</label> <div class="controls"> <input name="url" id="url" type="text" value="" placeholder="Your UR L" class="form-control"/> <span class="help-block">{{errorMessage 'url'}}</span> </div> </div> <div class="form-group {{errorClass 'title'}}"> <label class="control-label" for="title">Title</label> <div class="controls"> <input name="title" id="title" type="text" value="" placeholder="Nam e your post" class="form-control"/> <span class="help-block">{{errorMessage 'title'}}</span> </div> </div> <input type="submit" value="Submit" class="btn btn-primary"/> </form> </template> / / / _ client templates posts post . submit html è Notez que nous transmettons des param . helper Cela nous permet de r tres é ê utiliser le m comportement selon le param è ( . , changera ce qui , , à son tour title ) à chaque en modifiant son . : rendre ces helpers r Nous utiliserons Session pour stocker un objet ' , me helper chaque fois et tre Abordons maintenant la partie amusante messages d erreurs potentiels url respectivement ' é . ellement fonctionnels postSubmitErrors contenant tous les Pendant que l utilisateur interagit avec le formulaire , cet objet mettra à jour activement la mise en page et le contenu du . formulaire , En premier lieu ' nous initialiserons l objet à chaque fois que le template ' ' ' postSubmit Cela assure que l utilisateur ne verra pas d anciens messages d erreur laiss é s par une éé. est cr éé pr c . dente visite de cette page é Nous d field . finirons ensuite nos deux helpers de template 'ù lieu d o é ). ( ù field o est soit url ou title t selon le on appelle le helper Alors que pr Session.get('postSubmitErrors') de éé Ils regardent tous les deux la propri errorMessage ' - renvoie simplement lui m sence d un message et renvoie has-error ' ê , errorClass é me le message v rifie la . s il en existe un Template.postSubmit.onCreated(function() { Session.set('postSubmitErrors', {}); }); Template.postSubmit.helpers({ errorMessage: function(field) { return Session.get('postSubmitErrors')[field]; }, errorClass: function (field) { return !!Session.get('postSubmitErrors')[field] ? 'has-error' : ''; } }); //... / / / _ client templates posts post . submit js Vous pouvez tester que nos helpers fonctionnent normalement en ouvrant le navigateur et en tapant la ligne de code suivante : Session.set('postSubmitErrors', {title: 'Attention ! Intrusion détectée. Le s robots-chiens sont lâchés.'}); Browser console Code rouge ! Code rouge ! La prochaine é tape est de hooker cet objet de session , Avant de faire ça ' regarde l objet , savoir postSubmitErrors é nous allons cr er une nouvelle fonction post , et renvoie un objet errors si les champs title ou url ): //... validatePost = function (post) { var errors = {}; if (!post.title) errors.title = "Please fill in a headline"; } return errors; //... / / . lib collections posts js dans posts.js qui contenant toutes les erreurs pertinentes sont manquant if (!post.url) errors.url = "Please fill in a URL"; validatePost . au formulaire ( à 'é é Nous appellerons cette fonction depuis le helper d v nement postSubmit : 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() }; var errors = validatePost(post); if (errors.title || errors.url) return Session.set('postSubmitErrors', errors); Meteor.call('postInsert', post, function(error, result) { // affiche l'erreur à l'utilisateur et s'interrompt if (error) return throwError(error.reason); // affiche ce résultat mais route quand même if (result.postExists) throwError('This link has already been posted'); } }); Router.go('postPage', {_id: result._id}); }); / / / _ client templates posts post Notez que nous utilisons é pr , sente . submit js return ' é pour interrompre l ex é pas parce que nous voulons r cution du helper si une erreur est . ellement renvoyer cette valeur quelque part Pris la main dans le sac. Validation côté serveur ' . Nous n avons pas tout à fait fini ' - mais qu en est il du serveur ? è Apr , s tout é vide manuellement en appelant la m ' ê M ' ' ' é Nous validons la pr ' ' quelqu un pourrait toujours essayer d entrer un post thode postInsert ê ' é depuis l int me fonction é rieur de la m , thode . depuis la console du navigateur ' , me si nous n avons pas besoin d afficher de messages d erreur sur le serveur toujours utiliser la m , sence d une URL et d un titre sur le client ' nous pouvons validatePost . Sauf que cette fois, nous l appellerons aussi 'é é pas seulement depuis le helper d v nement : Meteor.methods({ postInsert: function(postAttributes) { check(this.userId, String); check(postAttributes, { title: String, url: String }); var errors = validatePost(postAttributes); if (errors.title || errors.url) throw new Meteor.Error('invalid-post', "You must set a title and URL f or your post"); var postWithSameLink = Posts.findOne({url: postAttributes.url}); if (postWithSameLink) { return { postExists: true, _id: postWithSameLink._id } } var user = Meteor.user(); var post = _.extend(postAttributes, { userId: user._id, author: user.username, submitted: new Date() }); var postId = Posts.insert(post); } }); return { _id: postId }; / / . lib collections posts js Une fois de plus é devez d , les utilisateurs ne devraient normalement jamais voir ce message finir un titre et une URL pour votre post ' ». ' « : ' Cela ne s affichera que si quelqu un veut é contourner l interface utilisateur que nous avons m ticuleusement mise en place , et utiliser . directement la console à la place , Pour tester ça Vous ' ouvrez la console du navigateur et essayez d entrer un post sans URL : Meteor.call('postInsert', {url: '', title: 'No URL here!'}); , Si nous avons fait notre travail proprement effrayant avec le message « é Vous devez d é vous devriez voir en retour une flop e de code finir un titre et une URL pour votre post Commit 9-4 ». . Validation du contenu du post au moment de la soumission Voir sur GitHub ' Lancer l instance Validation des éditions , Pour arrondir les angles ’é d . dition ê nous allons aussi appliquer la m Le code sera plut ô . t similaire ' me validation pour notre formulaire D abord le template : <template name="postEdit"> <form class="main form page"> <div class="form-group {{errorClass 'url'}}"> <label class="control-label" for="url">URL</label> <div class="controls"> <input name="url" id="url" type="text" value="{{url}}" placeholder=" Your URL" class="form-control"/> <span class="help-block">{{errorMessage 'url'}}</span> </div> </div> <div class="form-group {{errorClass 'title'}}"> <label class="control-label" for="title">Title</label> <div class="controls"> <input name="title" id="title" type="text" value="{{title}}" placeho lder="Name your post" class="form-control"/> <span class="help-block">{{errorMessage 'title'}}</span> </div> </div> <input type="submit" value="Submit" class="btn btn-primary submit"/> <hr/> <a class="btn btn-danger delete" href="#">Delete post</a> </form> </template> / / / _ client templates posts post . edit html Puis les helpers du template : Template.postEdit.onCreated(function() { Session.set('postEditErrors', {}); }); Template.postEdit.helpers({ errorMessage: function(field) { return Session.get('postEditErrors')[field]; }, errorClass: function (field) { return !!Session.get('postEditErrors')[field] ? 'has-error' : ''; } }); Template.postEdit.events({ 'submit form': function(e) { e.preventDefault(); var currentPostId = this._id; var postProperties = { url: $(e.target).find('[name=url]').val(), title: $(e.target).find('[name=title]').val() } var errors = validatePost(postProperties); if (errors.title || errors.url) return Session.set('postEditErrors', errors); Posts.update(currentPostId, {$set: postProperties}, function(error) { if (error) { // affiche l'erreur à l'utilisateur throwError(error.reason); } else { Router.go('postPage', {_id: currentPostId}); } }); }, 'click .delete': function(e) { e.preventDefault(); } }); if (confirm("Supprimer ce post ?")) { var currentPostId = this._id; Posts.remove(currentPostId); Router.go('postsList'); } / / / _ client templates posts post . edit js , Tout comme nous avons fait pour le formulaire de soumission de post ôé valider nos posts c pour t é . diter les posts . serveur À part , - , rappelez vous update mais un appel direct à ' nous voulons aussi é que nous n utilisons pas de m thode . depuis le client Cela signifie que nous devrons ajouter un nouveau callback deny à la place : //... Posts.deny({ update: function(userId, post, fieldNames, modifier) { var errors = validatePost(modifier.$set); return errors.title || errors.url; } }); //... / / . lib collections posts js ' Notez que l argument , mise à jour modifier se r ( . re au post existant validatePost ' que l objet post modifier.$set en entier . partielle qui concerne seulement ê è tre un probl -ê Vous remarquerez peut multiples callbacks cela veut dire que Dans ce cas , nous voulons valider la é é $set sur le contenu de la propri ) t de Posts.update({$set: {title: ..., url: ...}}) . comme dans devrait pas f ce pourquoi nous appelons Cela fonctionne parce que url éè post û, Bien s title r ê contient les deux m ' é é title mes propri t s et cela signifie que n importe quelle mise à jour ou url , ne fonctionnera pas mais en pratique ça ne . me ' tre que c est notre second callback ' ' ' ' deny . Lorsqu on ajoute de deny , l opération échouera si l un d entre eux renvoie true . Dans ce cas, update ' ne fonctionnera que s il cible seulement les champs ' url , ou si aucun des deux n est vide. title et Commit 9-5 Valider le contenu des posts lors des Voir sur GitHub ' é ditions Lancer l instance . Créer un Package Meteor Nous avons construit un pattern r ' ? 9.5 SIDEBAR é utilisable avec notre travail sur les erreurs , donc pourquoi é ne pas l empaqueter dans un paquet intelligent et le partager avec le reste de la communaut Meteor ê Pour ê , tre pr ts ' é nous devons nous assurer d avoir un compte d . , pouvez revendiquer le votre sur meteor com quand vous vous ' ê tes inscrits pour le livre ' ! veloppeur Meteor ' - le votre à celui ci è Premi , rement . Dans tous les cas tmeasday nous avons besoin de cr utiliser la commande éé Meteor a cr ' é l int rieur . , î dans ce chapitre vous pouvez substituez . é packages/tmeasday:errors/ , , – er une structure pour notre paquet é diter jà fait . é un dossier nomm comment il doit utiliser le paquet é vous devez conna tre votre nom meteor create --package tmeasday:errors Nous allons commencer par Vous mais il est fort probable que vous l ayez d d utilisateur car nous allons l utiliser intensivement dans ce chapitre Nous allons utiliser le nom d utilisateur ' . Nous pouvons pour cela . Notez que avec quelques fichiers à package.js , le fichier qui informe à Meteor ' et quels objets et fonctions il a besoin d exporter . Package.describe({ name: "tmeasday:errors", summary: "A pattern to display application errors to the user", version: "1.0.0" }); Package.onUse(function (api, where) { api.versionsFrom('0.9.0'); api.use(['minimongo', 'mongo-livedata', 'templating'], 'client'); api.addFiles(['errors.js', 'errors_list.html', 'errors_list.js'], 'client' ); if (api.export) api.export('Errors'); }); / : / . packages tmeasday errors package js é Quand on d é veloppe un paquet pour un usage destin pratique de remplir la section ( git du bloc , utilisateurs peuvent lire le code source readme Package.describe ) el ' c est une bonne avec les URL Git de votre repo https://github.com/tmeasday/meteor-errors.git . De cette manière, les comme ( é , au monde r ) î et ( ) en supposant que vous utilisez GitHub . - le lisez moi de votre paquet appara tra sur Atmosphere Ajoutons trois fichiers au paquet . Nous pouvons r é é cup rer ces trois fichiers de Microscope sans é è trop de changements à part pour certains espaces de noms propres et une API l plus claire g rement : Errors = { // Collection local (client seulement) collection: new Mongo.Collection(null), throw: function(message) { Errors.collection.insert({message: message, seen: false}) } }; / : / . packages tmeasday errors errors js <template name="meteorErrors"> <div class="errors"> {{#each errors}} {{> meteorError}} {{/each}} </div> </template> <template name="meteorError"> <div class="alert alert-danger" role="alert"> <button type="button" class="close" data-dismiss="alert">×</button > {{message}} </div> </template> / / : _ packages tmeasday errors errors . list html Template.meteorErrors.helpers({ errors: function() { return Errors.collection.find(); } }); Template.meteorError.rendered = function() { var error = this.data; Meteor.setTimeout(function () { Errors.collection.remove(error._id); }, 3000); }; / / : _ packages tmeasday errors errors . list js Tester le paquet avec Microscope Nous allons tester maintenant les choses localement avec Microscope pour nous assurer que notre code modifi é . fonctionne , Pour relier le paquet dans notre projet é nous ex cutons meteor add tmeasday:errors . Ensuite, nous avons besoin de supprimer les fichiers existants qui ont éé t rendu redondants par le nouveau paquet : rm client/helpers/errors.js rm client/templates/includes/errors.html rm client/templates/includes/errors.js Suppression des vieux fichiers via la console bash ' Une autre chose que nous avons besoin de faire est d effectuer quelques mises à jour ' mineures pour utiliser l API correctement {{> header}} {{> meteorErrors}} : / / / . client templates application layout html Meteor.call('postInsert', post, function(error, result) { if (error) { // affiche l'erreur à l'utilisateur Errors.throw(error.reason); / / / _ . client templates posts post submit js Posts.update(currentPostId, {$set: postProperties}, function(error) { if (error) { // Afficher l'erreur à l'utilisateur Errors.throw(error.reason); // affiche ce résultat mais route quand même if (result.postExists) Errors.throw('Ce lien à déjà été utilisé'); / / / _ client templates posts post . edit js Commit 9-5-1 é Cr ' Voir sur GitHub Une fois que ces changements ont - é original pr paquet . er des erreurs basiques et les relier éé t faits , Lancer l instance nous devrions r é é cup rer notre comportement . Ecrire des Tests è é La premi re é tape quand on d 'é la suivante est d , veloppe un paquet est de le tester dans une application mais . crire une suite de test qui teste proprement le comportement du paquet Meteor propose Tinytest ( ) é é, un testeur de paquet int gr ' é qui rend facile l ex cution de ce type . de tests et permet de rester serein quand on partage notre paquet avec les autres é Cr é ons un fichier de test qui utilise Tinytest pour ex errors cuter des tests sur le code du paquet : Tinytest.add("Errors - collection", function(test) { test.equal(Errors.collection.find({}).count(), 0); Errors.throw('A new error!'); test.equal(Errors.collection.find({}).count(), 1); Errors.collection.remove({}); }); Tinytest.addAsync("Errors - template", function(test, done) { Errors.throw('A new error!'); test.equal(Errors.collection.find({}).count(), 1); // render the template UI.insert(UI.render(Template.meteorErrors), document.body); Meteor.setTimeout(function() { test.equal(Errors.collection.find({}).count(), 0); done(); }, 3500); }); / / : _ packages tmeasday errors errors é Dans ces tests nous v ' è qu une deuxi . tests js rifions que les fonctions basiques é me v rification que le code é Nous ne couvrirons pas les sp ' n est pas encore finalis é cificit é 'é s d rendered , fonctionnent ainsi . dans le template fonctionne encore criture des tests de paquets Meteor ici e et hautement changeante é. Meteor.Errors ), ( ' comme l API mais heureusement son fonctionnement est assez bien expliqu Pour dire à Meteor comment ex é cuter les tests dans package.js , utilisez le code suivant : Package.onTest(function(api) { api.use('tmeasday:errors', 'client'); api.use(['tinytest', 'test-helpers'], 'client'); api.addFiles('errors_tests.js', 'client'); }); / / : . packages tmeasday errors package js Commit 9-5-2 . Ajout des tests du paquet Voir sur GitHub é Puis nous pouvons ex cuter les tests avec : meteor test-packages tmeasday:errors Terminal ' Lancer l instance Passer tous les tests Déployer le paquet Maintenant , é nous voulons d livrer le paquet et le rendre disponible à tout le monde faisons ça en le mettant sur le serveur de paquets de Meteor Atmopshere Nous é et en le d ployant sur . , Heureusement lancer , . ' c est tr è . s facile Il suffit de se rendre dans le dossier du paquet meteor publish --create : ( via ) cd , et de cd packages/tmeasday:errors meteor publish --create Terminal é Maintenant que le paquet est d directement é, ploy nous pouvons le supprimer du projet puis le rajouter : rm -r packages/errors meteor add tmeasday:errors Terminal ( lanc é ' ) depuis la racine de l application Commit 9-5-4 é Paquet supprim Voir sur GitHub éé Maintenant nous devrions voir Meteor t é Bien jou ! l é du dossier de d veloppement ' Lancer l instance è charger notre paquet pour la toute premi . re fois ' Comme d habitude avec les apart continuer ( - é, s ' - ' assurez vous d annuler les changements avant de ou sinon assurez vous d en tenir compte en suivant le reste du livre ). Commentaires 10 ' é Le but d un site de nouvelles sociales est de cr é er une communaut ' , d utilisateurs . difficile à atteindre sans fournir aux gens un moyen de communiquer entre eux donc dans ce chapitre ajouter les commentaires ! Nous allons Nous allons commencer par ajouter une nouvelle collection pour y enregistrer les commentaires et quelques donn é es basiques pr é Comments = new Mongo.Collection('comments'); / / . lib collections comments js é . enregistr es et cela sera // Données préenregistrées if (Posts.find().count() === 0) { var now = new Date().getTime(); // crée deux utilisateurs var tomId = Meteor.users.insert({ profile: { name: 'Tom Coleman' } }); var tom = Meteor.users.findOne(tomId); var sachaId = Meteor.users.insert({ profile: { name: 'Sacha Greif' } }); var sacha = Meteor.users.findOne(sachaId); var telescopeId = Posts.insert({ title: 'Introducing Telescope', userId: sacha._id, author: sacha.profile.name, url: 'http://sachagreif.com/introducing-telescope/', submitted: new Date(now - 7 * 3600 * 1000) }); Comments.insert({ postId: telescopeId, userId: tom._id, author: tom.profile.name, submitted: new Date(now - 5 * 3600 * 1000), body: "C'est un projet intéressant Sacha, est-ce-que je peux y participe r ?" }); Comments.insert({ postId: telescopeId, userId: sacha._id, author: sacha.profile.name, submitted: new Date(now - 3 * 3600 * 1000), body: 'Bien sûr Tom !' }); Posts.insert({ title: 'Meteor', userId: tom._id, author: tom.profile.name, url: 'http://meteor.com', submitted: new Date(now - 10 * 3600 * 1000) }); Posts.insert({ title: 'The Meteor Book', userId: tom._id, } author: tom.profile.name, url: 'http://themeteorbook.com', submitted: new Date(now - 12 * 3600 * 1000) }); / . server fixtures js ' ' N oublions pas de publier dans notre nouvelle collection et d y souscrire : Meteor.publish('posts', function() { return Posts.find(); }); Meteor.publish('comments', function() { return Comments.find(); }); / . server publications js Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return [Meteor.subscribe('posts'), Meteor.subscribe('comments')]; } }); / . lib router js Commit 10-1 Avec la collection de commentaires Voir sur GitHub é Notez que pour d é clencher ce code de donn , , la publication … la so ' Lancer l instance es pr é enregistr é , es il faudra faire un meteor reset é pour remettre à z é . ro votre base de donn es é cr Apr è s avoir r é é, initialis ' n oubliez pas de . er un nouveau compte et de vous reconnecter ' Nous avons tout d abord cr dans la base de donn é , es é . es utilisateur sur le premier post ' ( ( un couple d utilisateurs ), factices id et nous pourrons utiliser leur dehors de la base de donn l utilisateur ' éé ) , é Ensuite nous avons ajout é nous les avons enregistr plus tard pour les r éé f s rencer en un commentaire pour chaque en liant le commentaire avec le post ( postId via ) et avec userId . Nous avons aussi ajouté une date de publication et un body à via , chaque commentaire Nous avons aussi am author , un champ dénormalisé. en plus de é lior é notre router pour attendre un tableau . ( array ) contenant à la fois les commentaires et les souscriptions aux posts Afficher les commentaires ' è C est tr ' é , s bien d ajouter des commentaires à la base de donn afficher sur la page de discussion , familier . , Heureusement et vous devriez avoir une id é e des <template name="postPage"> <div class="post-page page"> {{> postItem}} <ul class="comments"> {{#each comments}} {{> commentItem}} {{/each}} </ul> </div> </template> / / / _ client templates posts post . page html é es mais nous voulons aussi les ce processus devrait vous tapes à r é aliser : ê tre maintenant Template.postPage.helpers({ comments: function() { return Comments.find({postId: this._id}); } }); / / / _ client templates posts post é Nous ins . page js {{#each comments}} rons le bloc é , rieur du template de post ' é s à ce post via l attribut templates de template est postId . Sachant ce que nous avons appris à propos des helpers et de Spacebars commentaire est plut this donc comments . Pour trouver les commentaires pertinents, nous vérifions un post dans le helper ceux qui sont li ' à l int ô . t simple é Nous allons cr ' er un nouveau dossier , é repr senter un comments ' é à l int rieur pour y stocker toute l information de nos commentaires et un nouveau commentItem : <template name="commentItem"> <li> <h4> <span class="author">{{author}}</span> <span class="date">on {{submittedText}}</span> </h4> <p>{{body}}</p> </li> </template> / / / _ client templates comments comment . item html é Cr ons donc un rapide helper de template pour repr format plus convivial : Template.commentItem.helpers({ submittedText: function() { return this.submitted.toString(); } }); é senter notre date submitted dans un / / / _ client templates comments comment . item js Nous allons ensuite afficher le nombre de commentaires de chaque post : <template name="postItem"> <div class="post"> <div class="post-content"> <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3> <p> Rédigé par {{author}}, <a href="{{pathFor 'postPage'}}">{{commentsCount}} commentaires</a> {{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}} </p> </div> <a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Comment er</a> </div> </template> / / / _ client templates posts post Et ajouter le helper . item html commentsCount à post_item.js : Template.postItem.helpers({ ownPost: function() { return this.userId === Meteor.userId(); }, domain: function() { var a = document.createElement('a'); a.href = this.url; return a.hostname; }, commentsCount: function() { return Comments.find({postId: this._id}).count(); } }); / / / _ client templates posts post . item js Commit 10-2 Affichage des commentaires sur ê postPage `. ' Voir sur GitHub Vous devriez maintenant ` Lancer l instance ' quelque chose comme ça tre capable d afficher nos commentaires pr : é é enregistr s et voir Affichage des commentaires Publier des commentaires é Ajoutons un moyen pour nos utilisateurs de cr . er de nouveaux commentaires é é Le proc d é nous allons suivre est similaire à la façon dont nous avons permis à nos utilisateurs de cr . nouveaux posts Nous allons commencer par ajouter un bouton de publication en bas de chaque post : que er de <template name="postPage"> <div class="post-page page"> {{> postItem}} <ul class="comments"> {{#each comments}} {{> commentItem}} {{/each}} </ul> {{#if currentUser}} {{> commentSubmit}} {{else}} <p>Please log in to leave a comment.</p> {{/if}} </div> </template> / / / _ client templates posts post En ensuite cr . page html é er le template du formulaire de commentaire : <template name="commentSubmit"> <form name="comment" class="comment-form form"> <div class="form-group {{errorClass 'body'}}"> <div class="controls"> <label for="body">Réagir à ce post</label> <textarea name="body" id="body" class="form-control" rows="3"></text area> <span class="help-block">{{errorMessage 'body'}}</span> </div> </div> <button type="submit" class="btn btn-primary">Add Comment</button> </form> </template> / / / _ client templates comments comment , Pour publier nos commentaires è comment_submit.js publications de posts qui op : . submit html é nous appelons une M ' è re d une mani thode comment dans re similaire à ce que nous avons fait pour les Template.commentSubmit.onCreated(function() { Session.set('commentSubmitErrors', {}); }); Template.commentSubmit.helpers({ errorMessage: function(field) { return Session.get('commentSubmitErrors')[field]; }, errorClass: function (field) { return !!Session.get('commentSubmitErrors')[field] ? 'has-error' : ''; } }); Template.commentSubmit.events({ 'submit form': function(e, template) { e.preventDefault(); var $body = $(e.target).find('[name=body]'); var comment = { body: $body.val(), postId: template.data._id }; var errors = {}; if (! comment.body) { errors.body = "Please write some content"; return Session.set('commentSubmitErrors', errors); } Meteor.call('commentInsert', comment, function(error, commentId) { if (error){ throwError(error.reason); } else { $body.val(''); } }); } }); / / / _ client templates comments comment . submit js éé Tout comme nous avons pr , serveur c é demment mis en place une M é nous allons mettre en place une M , commentaires ' thode Meteor , s assurer que tout est conforme commentaire dans la collection ‘ ’. comments thode Meteor comment é et finalement ins pour cr post côté é er nos rer le nouveau Comments = new Mongo.Collection('comments'); Meteor.methods({ commentInsert: function(commentAttributes) { check(this.userId, String); check(commentAttributes, { postId: String, body: String }); var user = Meteor.user(); var post = Posts.findOne(commentAttributes.postId); if (!post) throw new Meteor.Error('invalid-comment', 'Vous devez commenter sur un post'); comment = _.extend(commentAttributes, { userId: user._id, author: user.username, submitted: new Date() }); } }); / return Comments.insert(comment); / . lib collections comments js Commit 10-3 Avec le formulaire de publication de commentaires ' Voir sur GitHub , Cela ne fait rien de bien fantaisiste le commentaire a un corps ( ), body ' ' Lancer l instance ' é, on s assure simplement que l utilisateur est connect é et qu il est li . au post que Le formulaire de publication de commentaires Contrôle de la publication de commentaires 'é Dans l , tat des choses é. clients connect ' s nous publions tous les commentaires à travers les posts à tous les é Cela semble un peu d . é qu une petite partie de ces donn è raisonnable . es à tout moment Apr , s tout ' é Am , Si nous y pensons commentaires é. ler exactement quels commentaires sont publi ù le seul moment o ' re . é ' , Jusqu à maintenant re dont nous souscrivons à nos , nous avons souscrit au niveau du routeur é û tre doit bien s é besoin de d . s à ce post particulier ' ce qui signifie é. es d un seul coup lorsque le routeur est initialis é Mais nous voulons maintenant que notre souscription d è et nous avons è que nous chargeons toutes nos donn param , de à la page individuelle d un post tape consistera à changer la mani commentaires ' è est lorsqu un utilisateur acc è é s on a besoin de souscrire à la publication de nos besoin de charger uniquement les commentaires reli La premi ellement liorons donc nos publications et ô souscriptions pour contr é nous n utilisons r ' ' è pende d un param . r pouvoir changer à n importe quel moment , tre chemin et ce Nous allons donc avoir . placer notre code de souscription du niveau du routeur à celui des routes é Cela a une autre cons , appli quence : é au lieu de charger nos donn es quand nous initialisons notre nous les chargerons maintenant à chaque fois que notre route sera atteinte ' . Cela veut ' , dire que vous aurez maintenant des temps de chargement pendant l utilisation de l appli ' mais c est un inconv ' é l ensemble des donn è Premi , rement configure nient in é vitable à moins que vous ne pr é es au d but nous allons arr en supprimant éé nous avions pr é c demment ê é é voyiez de charger d finitivement . é ter de pr charger tous les commentaires dans le bloc Meteor.subscribe('comments') ): ( autrement dit revenir à ce que Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return Meteor.subscribe('posts'); } }); / . lib router js Et nous allons ajouter une nouvelle fonction waitOn de niveau route pour la route postPage : //... Router.route('/posts/:_id', { name: 'postPage', waitOn: function() { return Meteor.subscribe('comments', this.params._id); }, data: function() { return Posts.findOne(this.params._id); } }); //... / . lib router js Nous passons this.params._id ' ' . en tant qu argument à la souscription é nouvelle information pour s assurer de restreindre notre lot de donn appartenant au post courant Utilisons donc cette es aux commentaires : Meteor.publish('posts', function() { return Posts.find(); }); Meteor.publish('comments', function(postId) { check(postId, String); return Comments.find({postId: postId}); }); / . server publications js Commit 10-4 / … Avec une simple publication souscription pour les comment Voir sur GitHub ' ' Il n y a qu un seul probl ' è me , ' Lancer l instance ' , quand nous retournons à la page d accueil nos posts n ont aucun commentaires : é il pr tend que tous Nos commentaires ont disparu ! Compter les commentaires î ô La raison va nous appara tre bient : nous ne chargeons les commentaires que sur la postPage , donc lorsque nous appelons Comments.find({postId: this._id}) route helper é . sultat é La meilleure façon de r 'ê si vous n ' dans le commentsCount , Meteor ne peut pas trouver les données nécessaires côté client pour nous fournir un r ( t claire !). é, , r de savoir ce que cela veut dire é ne vous inqui , Bien que comme nous allons le voir complexifi normaliser le nombre de commentaires sur le post û tes pas s s occupe de vous é gler cela est de d n ' é le prochain apart é è notre code s en retrouve l é é le b , tez pas g rement fice de performance que nous retirons de ne pas avoir à publier tous les . commentaires pour afficher le post en vaut le coup é é commentsCount Nous allons terminer ça en ajoutant une propri t é à la structure de donn post . Nous allons commencer par mettre à jour nos données préenregistrées de posts du meteor reset faire ) è : apr s pour les recharger –' é n oubliez pas de recr er votre compte utilisateur es ( et // Données préenregistrées if (Posts.find().count() === 0) { var now = new Date().getTime(); // create two users var tomId = Meteor.users.insert({ profile: { name: 'Tom Coleman' } }); var tom = Meteor.users.findOne(tomId); var sachaId = Meteor.users.insert({ profile: { name: 'Sacha Greif' } }); var sacha = Meteor.users.findOne(sachaId); var telescopeId = Posts.insert({ title: 'Introducing Telescope', userId: sacha._id, author: sacha.profile.name, url: 'http://sachagreif.com/introducing-telescope/', submitted: new Date(now - 7 * 3600 * 1000), commentsCount: 2 }); Comments.insert({ postId: telescopeId, userId: tom._id, author: tom.profile.name, submitted: new Date(now - 5 * 3600 * 1000), body: 'Interesting project Sacha, can I get involved?' }); Comments.insert({ postId: telescopeId, userId: sacha._id, author: sacha.profile.name, submitted: new Date(now - 3 * 3600 * 1000), body: 'Bien sûr Tom !' }); Posts.insert({ title: 'Meteor', userId: tom._id, author: tom.profile.name, url: 'http://meteor.com', submitted: new Date(now - 10 * 3600 * 1000), commentsCount: 0 }); Posts.insert({ title: 'The Meteor Book', userId: tom._id, author: tom.profile.name, url: 'http://themeteorbook.com', submitted: new Date(now - 12 * 3600 * 1000), commentsCount: 0 }); } / . server fixtures js ' , Comme d habitude lorsque nous mettons à jour notre fichier fixtures meteor reset , Puis sur votre base de donn ' é vous devez lancer é. e pour vous assurer qu il sera relanc é nous nous assurons que tous les nouveaux posts d butent avec 0 commentaire : //... var post = _.extend(postAttributes, { userId: user._id, author: user.username, submitted: new Date(), commentsCount: 0 }); var postId = Posts.insert(post); //... / / . lib collections posts js Ensuite nous mettons à jour le commentsCount ' nouveau commentaire en utilisant l op é num rique de un ): é ' é qui s y rapporte lorsque nous cr rateur Mongo $inc ( qui incr é ons un mente un champ //... comment = _.extend(commentAttributes, { userId: user._id, author: user.username, submitted: new Date() }); // update the post with the number of comments Posts.update(comment.postId, {$inc: {commentsCount: 1}}); return Comments.insert(comment); //... / / . lib collections comments js , Finalement nous pouvons simplement supprimer le helper commentsCount de client/templates/posts/post_item.js , puisque le champ est maintenant directement . disponible sur le post Commit 10-5 é D . normalisation du nombre de commentaires dans le post ' Voir sur GitHub Lancer l instance , Maintenant que les utilisateurs peuvent discuter entre eux . nouveaux commentaires é impl , Et vous savez quoi menter les notifications pour é viter ça ! ' ce serait dommage qu ils ratent les le prochain chapitre vous montrera comment La Dénormalisation é D ' normaliser des donn , é d autres termes é donn d é é es veut dire ne pas stocker ces donn es sous une forme normalisation veut dire avoir de multiples copies du m ê “ ”. normale En me morceau de . e dans de multiples endroits , Dans le dernier chapitre ' l objet ‘ ’ post pour vue de la mod é nous avons d ' é é é le total du nombre de commentaires dans . é lisation des donn é , es é les consid normalis viter d avoir à constamment charger tous les commentaires compter les commentaires ad ô ). ( t en omettant rations de performance normaliser est souvent synonyme de travail suppl , notre exemple ' è chaque fois que l on ajoute ou enl ' é rappeler de mettre à jour l article concern ( ) total des commentaires . reste juste ' ' é , ' l approche classique a é mentaire pour le d ve un commentaire , . veloppeur il faudra pour s assurer que le champs é Dans galement se commentsCount é C est exactement pour cela que les bases de donn ' es œ . telles que MySQL voient cette approche d un mauvais Cependant ' D un point de ceci est redondant puisque nous pourrions plut quats à tout moment pour calculer cette valeur é D 10.5 SIDEBAR il é é galement ses inconv : nients éé sans une propri t commentsCount , nous devrions envoyer tous les commentaires en permanence vers le serveur , afin de pouvoir les compter nous é viter tout cela é ce que nous faisions au d . part é La d normalisation permet de . Une Publication Spéciale é Il serait possible de cr commentaires qui nous int ' ' ( é ressent que l on peut voir actuellement Mais il conviendrait d é er une publication sp 'é , ê en utilisant des requ é é éé s cr ' par exemple le total des commentaires d articles tudier si la complexit l emporterait sur les difficult ciale qui envoie uniquement le total des ' tes d agr ' é d un tel code de publication é es par la d … normalisation ). gation sur le serveur û, Bien s ' l int é r é de telles consid é grit des donn ' : rations sont propre à l application ' é es est d importance capitale est bien plus important et de priorit é é bien plus si vous , alors é lev é ù crivez du code o é é viter les inconsistances de donn e que des gains de performance es . Intégrer des Documents ou Utiliser des Collections Multiples Si vous ê , tes familier avec Mongo -ê vous avez peut tre éé t éé surpris de voir que nous avons cr : une seconde collection uniquement pour les commentaires commentaires dans une liste dans le ' è Il s av ' '? post ' . . collection find ()). ' ' et est tr Par exemple deny : ' è s efficace lorsque l on it ê ' ' é l on op è . , fonctionnent au niveau du document rait au niveau du le Protocole de Donn niveau du document , article , ' ’. é – é es Distribu es ( ) DDP è op cela voudrait dire que si è le serveur enverrait la liste compl . , Par exemple nous assurant ainsi que toute chose qui serait plus complexe si re au niveau des attributs au plus au ' comments était une propriété d un te des commentaires mis à jour pour cet é. ' é è Mongo sugg û ' re d int ' , ù o ' l acc è ' ler au niveau des , on . ' é grer les documents dans l ordre pour r é cup . rer , Cependant compte l architecture de Meteor client ô aliser à moins que les commentaires ne soient dans leur é é teuses pour les r ' sultat de si l on voulait paginer les commentaires d un article trouverait cela difficile à r propre collection é le r re sur un tableau d objets dans Les publications et souscriptions sont beaucoup plus faciles à contr documents ( post article vers chaque client connect co re sur un curseur me lorsque l on it modification de commentaire individuel soit correcte 4. grer les Il n en est pas de m un plus gros document 2. allow è {{#each}} La fonction 3. é pourquoi ne pas juste int re que de nombreux outils que Meteor nous fournis marchent bien mieux lorsqu ils fonctionnent au niveau d une collection 1. : ê ceci rev ' ê duire le nombre de requ ' tes t moins d importance lorsque l on prend en ' la plupart du temps l on recherchera les commentaires sur le s à la base de donn é es est essentiellement gratuite . Les Inconvénients de la Dénormalisation é On peut facilement d ' é fendre le fait qu il ne faille pas d é examiner les arguments contre la d , normalisation . You Should Never Use MongoDB par Sarah Mei é . normaliser ses donn es Pour nous vous recommandons Why Les Notifications 11 , Maintenant que les utilisateurs peuvent commenter les articles de chacun ' il serait bien de les é. avertir qu une conversation a commenc Pour ce faire , ' ' ' nous notifierons à l auteur de l article qu il y a eu un commentaire . , et nous lui fournirons un lien pour voir le commentaire ' é C est sur ce type de fonctionnalit é par d ' , faut que Meteor resplendit : . é nous afficherons ces notifications instantan ' î d attendre que l utilisateur rafra chisse la page ou v é é parce que Meteor est en temps r ment ' Nous n avons pas besoin , rifie par un quelconque moyen î pouvons simplement faire appara tre de nouvelles notifications sans m é sp el ê é me nous crire de code . cial Créer des Notifications Nous allons cr ' é les notifications pourront ' ê tre é é é ons notre collection é qui ins ' ' , Dans le future é tendues pour couvrir beaucoup d autres sc l instant ce sera suffisant pour garder les utilisateurs inform Cr . er une notification quand quelqu un commente sur vos articles , narios s de ce qu il se passe mais pour . ' Notifications , ainsi qu une fonction createCommentNotification rera une notification correspondante pour chaque nouveau commentaire sur un de . vos articles , Puisque nous allons mettre à jour les notifications depuis le client que notre appel ' allow est blind é. ' l utilisateur qui lance l appel é . modifi é Nous allons donc v update poss è rifier que nous devons nous assurer : 'ê de la notification qui est en train d e ' . l utilisateur essaie seulement de mettre à jour un seul champ é é read ce seul champ est la propri t de nos notifications . tre Notifications = new Mongo.Collection('notifications'); Notifications.allow({ update: function(userId, doc, fieldNames) { return ownsDocument(userId, doc) && fieldNames.length === 1 && fieldNames[0] === 'read'; } }); createCommentNotification = function(comment) { var post = Posts.findOne(comment.postId); if (comment.userId !== post.userId) { Notifications.insert({ userId: post.userId, postId: post._id, commentId: comment._id, commenterName: comment.author, read: false }); } }; / / . lib collections notifications js Comme les articles ou commentaires . client et serveur ' , Notifications cette collection , ' nous autorisons é , galement les mises à jours é d habitude de bien limiter les permissions aux propres donn é galement cr , é commente d é Nous cr é compl éé ê é tre notifi , à ce moment jà des commentaires dans une m é . thode pour appeler notre fonction Comments.insert(comment); ’ _id notre fonction ' en s assurant comme ' . es de l utilisateur ' par t è et ins ôé thode c é ter cette m sauvegarder l t ' une simple fonction qui surveille l article que l utilisateur termine qui devrait é ons d ôé e c Comme nous avons besoin de mettre à jour les notifications une fois que l utilisateur les a vues Nous avons é sera partag . re une nouvelle notification serveur , donc nous pouvons juste Nous remplacerons return comment._id = Comments.insert(comment) du commentaire nouvellement cr createCommentNotification : éé , dans une variable afin de puis appellerons Comments = new Mongo.Collection('comments'); Meteor.methods({ commentInsert: function(commentAttributes) { //... comment = _.extend(commentAttributes, { userId: user._id, author: user.username, submitted: new Date() }); // met à jour le post avec le nombre de commentaires Posts.update(comment.postId, {$inc: {commentsCount: 1}}); // crée le commentaire et enregistre l'id comment._id = Comments.insert(comment); // crée maintenant une notification, informant l'utilisateur qu'il y a e u un commentaire createCommentNotification(comment); return comment._id; } }); / / . lib collections comments js Publions é galement les notifications : Meteor.publish('posts', function() { return Posts.find(); }); Meteor.publish('comments', function(postId) { check(postId, String); return Comments.find({postId: postId}); }); Meteor.publish('notifications', function() { return Notifications.find(); }); / . server publications js Et souscrivons sur le client : Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return [Meteor.subscribe('posts'), Meteor.subscribe('notifications')] } }); / . lib router js ' Commit 11-1 Ajout d une collection basique de notifications Voir sur GitHub . ' Lancer l instance Affichage des notifications ' -ê Maintenant nous pouvons continuer et ajouter une liste de notifications à l en t . te <template name="header"> <nav class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="col lapse" data-target="#navigation"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="{{pathFor 'postsList'}}">Microscope</a> </div> <div class="collapse navbar-collapse" id="navigation"> <ul class="nav navbar-nav"> {{#if currentUser}} <li> <a href="{{pathFor 'postSubmit'}}">Submit Post</a> </li> <li class="dropdown"> {{> notifications}} </li> {{/if}} </ul> <ul class="nav navbar-nav navbar-right"> {{> loginButtons}} </ul> </div> </nav> </template> / / / . client templates includes header html é Et cr è er les mod fichier les de notifications ) notifications.html : et notificationItem ( ê ils partageront un m me <template name="notifications"> <a href="#" class="dropdown-toggle" data-toggle="dropdown"> Notifications {{#if notificationCount}} <span class="badge badge-inverse">{{notificationCount}}</span> {{/if}} <b class="caret"></b> </a> <ul class="notification dropdown-menu"> {{#if notificationCount}} {{#each notifications}} {{> notificationItem}} {{/each}} {{else}} <li><span>No Notifications</span></li> {{/if}} </ul> </template> <template name="notificationItem"> <li> <a href="{{notificationPostPath}}"> <strong>{{commenterName}}</strong> a commenté votre article </a> </li> </template> / / / . client templates notifications notifications html ' é Nous pouvons voir que l id éé t é, comment , Ensuite ' ' é. et le nom de l utilisateur qui l a comment nous avons besoin de nous assurer que nous s notifications dans notre helper ' ' e est de fournir dans chaque notification un lien vers l article qui a , é lectionnons la bonne liste de et mettons à jour les notifications comme . l utilisateur clique sur le lien vers lequel elles pointent “ ” lues quand Template.notifications.helpers({ notifications: function() { return Notifications.find({userId: Meteor.userId(), read: false}); }, notificationCount: function(){ return Notifications.find({userId: Meteor.userId(), read: false}).count( ); } }); Template.notificationItem.helpers({ notificationPostPath: function() { return Router.routes.postPage.path({_id: this.postId}); } }); Template.notificationItem.events({ 'click a': function() { Notifications.update(this._id, {$set: {read: true}}); } }); / / / . client templates notifications notifications js Commit 11-2 ' -ê Afficher des notifications dans l en t ' Voir sur GitHub Lancer l instance é Vous pouvez penser que les notifications ne sont pas si diff è leur structure est tr collection client / . s similaire navigateurs et des diff - , utilisateur principal ( : é anmoins une diff é . serveur synchronis e , rents appareils ouvrez un deuxi è chose comme ça : nous avons cr éé une î survivront au rafra chissement des ( ), disons Firefox et commentez sur un article que vous avez cr é ' et c est vrai que . me navigateur que vous avez laiss é: rence cl , , me compte utilisateur é rentes des erreurs Cela signifie que nos notifications sont persistantes et ê tant que nous gardons le m Essayez le é Il y a n . te ). ouvert dans Chrome éé cr é ez un nouveau compte avec votre compte utilisateur î Vous devriez voir appara tre quelque Afficher des notifications. Contrôler l'accès aux notifications . Les notifications fonctionnent bien , Cependant è il y a un petit probl me : nos notifications sont . publiques è Si vous avez encore votre deuxi dans une console navigateur , me navigateur ouvert ’ é essayez d ex : cuter le code suivant ❯ 1 Notifications.find().count(); Console du navigateur Ce nouvel utilisateur ' ( notification qu il voit dans la collection . ) é celui qui a comment . ne devrait pas avoir de notifications Notifications La appartient en fait à notre utilisateur original è En marge de ces potentiels probl é, mes de confidentialit nous ne pouvons pas nous permettre ' é d avoir toutes les notifications des utilisateurs charg . utilisateurs , Sur un assez gros site î é cela pourrait surcharger la m é navigateur et faire appara tre des s rieux probl è pour sp é moire disponible du mes de performance è . me avec les publications Nous corrigeons ce probl es dans tous les navigateurs des autres . Nous pouvons utiliser nos publications é é cifier pr cis ment quelle partie de notre collection nous voulons partager dans . chaque navigateur , Pour accomplir cela publication de nous avons besoin de retourner un curseur diff é rent dans notre Notifications.find() . Plus exactement, nous voulons retourner un curseur ' . qui correspond aux notifications de l utilisateur courant , Faire cela est assez direct disponible via puisqu this.userdId : ’ publish une fonction a l ’ id ' de l utilisateur courant Meteor.publish('notifications', function() { return Notifications.find({userId: this.userId, read: false}); }); / . server publications js Commit 11-3 … Synchroniser uniquement les notifications pertinentes pou ' Voir sur GitHub Maintenant , é si nous v ê rifions dans nos deux fen é collections de notifications diff ❯ 1 Lancer l instance rentes : Notifications.find().count(); tres de navigateur , nous devrions voir deux Console du navigateur ❯ 0 utilisateur ) 1 Notifications.find().count(); Console du navigateur En fait , ( utilisateur ) 2 ê la liste de Notifications devrait m é d ( connectiez de votre application . me changer selon que vous vous authentifiez et vous é Tout cela parce que les publications sont republi automatiquement quand le compte utilisateur change . Notre application devient de plus en plus fonctionnelle ' es , et à mesure que des utilisateurs ' s inscrivent et ajoutent des liens nous courons le risque de finir avec une page d accueil sans . fin Nous allons nous occuper de ça dans le prochain chapitre en impl . pagination é mentant une Réactivité Avancée ' 11.5 SIDEBAR 'é Il est rare d avoir besoin d - crire du code de traçage vous m ê me , mais il est assur é de le comprendre pour comprendre la façon dont le processus de r fonctionne é ment utile é solution de d pendance . ' ' Imaginez que nous voulions tracer combien d amis Facebook de l utilisateur courant ont “ ” é aim é les d ' , l API . chaque article sur Microscope ' ' , tails de l authentification de l utilisateur avec Facebook ' et fait l analyse des donn ôé asynchrone c t é é Partons du principe que nous avons d . es pertinentes é é fait les appels appropri s vers “' j aime ”, getFacebookLikeCount(user, ' ' é La chose importante à retenir au sujet d une telle fonction est qu elle est vraiment non r é . et non temps r ê Elle fera une requ el ' , é te HTTP vers Facebook , rendra disponible à l application via un rappel asynchrone - ê une nouvelle fois par elle m , Pour corriger cela r é cup ' es active et les é mais la fonction ne s ex , - é é , rera des donn me quand le compte changera chez Facebook changera pas quand les donn sur Nous avons maintenant une fonction client qui retourne le nombre de url, callback) . jà travaill cutera pas et notre UI ne . es sous jacentes le feront é nous pouvons d marrer en utilisant fonction toutes les quelques secondes setInterval pour appeler notre : currentLikeCount = 0; Meteor.setInterval(function() { var postId; if (Meteor.user() && postId = Session.get('currentPostId')) { getFacebookLikeCount(Meteor.user(), Posts.find(postId).url, function(err, count) { if (!err) currentLikeCount = count; }); } }, 5 * 1000); ù Quel que soit le moment o é nous v rifions cette variable currentLikeCount , nous pouvons ' . nous attendre à avoir un nombre correct avec une marge d erreur de cinq secondes Nous : pouvons utiliser cette variable dans un helper comme suit Template.postItem.likeCount = function() { return currentLikeCount; } , Cependant rien ne dit à notre template de se recharger quand é Bien que la variable soit maintenant pseudo temps r ' elle n est pas r 'é de l . change ' - el du fait qu elle change par elle m ê , me é active donc elle ne peut pas tout à fait communiquer proprement avec le reste è cosyst currentLikeCount . me Meteor Réactivité de la surveillance : Computations (Calculs) é La r é activit de Meteor est contr ôé l , é e par des d pendances é des structures de donn es qui . surveillent un ensemble de calculs ' éé Comme nous l avons vu dans un pr code qui utilise des donn le template é es r c é . actives é dent apart é sur la r , Dans notre cas é, activit un calcul a un calcul est une section de éé t éé implicitement cr pour postItem , chaque helper de ce gestionnaire de template a sa propre partie . calculs Vous pouvez penser au calcul comme à la section de code qui é donn é es r . actives ) Quand la donn ' é e change , “' ” s occupe de la source de é ce sera ce calcul qui en sera inform invalidate() , et c est le calcul qui décide si quelque chose doit être fait. ( via Transformer une Variable en une Fonction Réactive Pour transformer notre variable currentLikeCount ' é en source de donn é avons besoin de surveiller tous les calculs qui l utilisent dans une d de la transformer de variable à fonction ( qui retourne une valeur ): es r é actives . pendance , nous Cela requiert var _currentLikeCount = 0; var _currentLikeCountListeners = new Tracker.Dependency(); currentLikeCount = function() { _currentLikeCountListeners.depend(); return _currentLikeCount; } Meteor.setInterval(function() { var postId; if (Meteor.user() && postId = Session.get('currentPostId')) { getFacebookLikeCount(Meteor.user(), Posts.find(postId), function(err, count) { if (!err && count !== _currentLikeCount) { _currentLikeCount = count; _currentLikeCountListeners.changed(); } }); } }, 5 * 1000); é Nous avons mis en place une d _currentLikeCountListeners , qui surveille tous currentLikeCount() les calculs dans lesquels _currentLikeCount pendance change , a éé t é. utilis changed() nous appelons la fonction qui invalide tous les calculs surveill Quand la valeur de é sur cette d é. s Ces calculs peuvent ensuite continuer et traiter le changement au cas par cas Si cela vous semble é r active simple ' ( , ê é tre une approche un peu r vous avez raison , ' . é barbative pour une simple source de donn é é et Meteor fournit des outils int ' gr s pour rendre ça un peu plus , tout comme vous n avez pas besoin d utiliser des calculs directement ). d habitude des autoruns exactement ce que notre - é reactive-var Il y a un paquet plate forme appel currentLikeCount() . fonction fait ' vous utilisez qui fait Si nous l ajoutons meteor add reactive-var ' , pendance Nous pouvons l utiliser pour simplifier notre code un petit peu : : es var currentLikeCount = new ReactiveVar(); Meteor.setInterval(function() { var postId; if (Meteor.user() && postId = Session.get('currentPostId')) { getFacebookLikeCount(Meteor.user(), Posts.find(postId), function(err, count) { if (!err) { currentLikeCount.set(count); } }); } }, 5 * 1000); Maintenant , ' , pour l utiliser cela fonctionnera comme avant ô é fournit un entrep aussi utile t r currentLikeCount.get() nous appellerons - é actif cl . . - dans notre helper et reactive-dict , qui Il y a aussi un autre paquet plate forme valeur ( presque exactement comme ) Session , qui peut être Comparer Tracker à Angular Angular est une biblioth gens de Google . Il est int ' è é que de rendu r é , Meteor à celle d Angular ). calculs ' , é t client seulement - car celles ci sont assez diff é é é s par des sources de donn ' é é. é é n es “é r é - Dans Angular , la r é activit é é activit ”( fonctions é ) qui es informent invalidate() . Notez é, es ont chang ' é la source de donn é . é, cutent habituellement quand ils sont invalid . s vous Tout ceci nous donne un haut é. ôé est contr l es clencher une invalidation pour d autres raisons pouvez les configurer pour se comporter comme vous le voulez le sur la r actives ' é cider de d bien que les calculs se re ex ô s computations Donc les sources de donn ralement quand les donn é pourrait potentiellement d niveau de contr é pendances quand elles ont besoin d appeler que bien que cela se passe g pendance de . le de Meteor utilise des blocs de code appel Ces calculs sont traqu par les bonnes rentes è explicitement toutes ses d , é velopp é prennent soin de les invalider quand c est appropri De plus d ressant de comparer l approche de surveillance de d Nous avons vu que le mod ( ôé actif c ' e par l objet scope . Un scope peut être imaginé comme un objet JavaScript simple avec plusieurs m é Quand vous voulez d é é thodes sp ' , pendre activement d une valeur dans un scope ' scope.$watch , en fournissant l expression qui vous intéresse é qui vous int ' ) resse ciales ' é et une fonction listener qui s ex . . vous appelez (' -- c est à dire la partie du scope cutera à chaque fois que la valeur de l expression change De retour sur notre exemple Facebook , é nous cririons : $rootScope.$watch('currentLikeCount', function(likeCount) { console.log('Current like count is ' + likeCount); }); û, Bien s ' r , tout comme vous ne mettez que rarement en place des calculs dans Meteor $watch n appelez pas souvent {{expressions}} et les é de r explicitement dans Angular puisque les directives ' vous ng-model configurent automatiquement des watches qui ensuite s occupent . actualiser la page Quand ce type de valeur r é , chaque watcher du scope ' é, scope.$apply() active a chang doit ê é. tre appel Ceci r éé value mais appelle seulement la fonction listener des veilleurs pour qui la é. valeur de l expression a chang scope.$apply() , scope éé r est similaire à ô plut é. valu s ô t que vous donner le contr Ceci dit , é ce l tre r éé le de dire pr é é cis ment quels listeners doivent ' ô ger manque de contr intelligent et efficace dans la mani ê ' dependency.changed() , excepté qu il agit au niveau du è é re dont il d termine pr é é cis ' é ê le donne à Angular l habilit d ê tre tre vraiment ment quels listeners doivent é. valu s , Avec Angular notre code de fonction getFacebookLikeCount() devrait ressembler à ça : Meteor.setInterval(function() { getFacebookLikeCount(Meteor.user(), Posts.find(postId), function(err, count) { if (!err) { $rootScope.currentLikeCount = count; $rootScope.$apply(); } }); }, 5 * 1000); , Certes ' é é Meteor s occupe du gros du travail pour nous et nous laisse b sans trop de travail de notre part . , Mais heureusement n é ficier de la r ’ é apprendre ces pratiques s av . si vous avez besoin de pousser les choses un peu plus loin é activit rera utile La Pagination 12 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 performances du nombre de nouveaux articles qui vont son envol ! ê é tre entr é quence sur les s dans le site au moment de éé Nous avons pr - c demment parl sous ensemble des donn é ôé de comment une collection c é , es du serveur et nous avons m t ê client peut contenir un me appris à r é aliser cela pour nos . collections de notifications et de commentaires À pr é , sent é. connect s Éventuellement é Pour r ' , nous publions encore tous nos articles d un seul coup , soudre cela , é, si des milliers de liens sont post s à tous les utilisateurs é ceci deviendra probl . nous avons besoin de paginer nos articles Ajouter plus d'articles è Premi , rement dans nos donn cette pagination ait un sens : é es de pr - é , installation ' . matique chargeons assez d articles pour que // 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: new Date(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: new Date(now - i * 3600 * 1000), commentsCount: 0 }); } / . server fixtures js è Apr é s avoir ex comme ça : cut é meteor reset é et relanc , votre appli vous devriez obtenir quelque chose Afficher des données factices. ' Commit 12-1 ' … é Ajout d assez d articles pour que la pagination soit n ce ' Voir sur GitHub Lancer l instance Pagination infinie é Nous allons impl menter une pagination de style è que nous voulons premi ' ” plus d articles , liste , rement afficher . inscrit en bas , 10 disons . et ainsi de suite ad infinitum 'é articles à l 10 , cran “ avec un lien charger é articles suppl ' é tre repr ' Ce que nous voulons dire par là c est mentaires dans la ô Cela signifie que nous pouvons contr è 'é ”. infini Cliquer sur le lien affichera pagination entier avec un simple param l “ è ler notre syst me de sentant le nombre d articles à afficher à . cran ' ' ' . sache combien d articles envoyer au client publication posts , dans le routeur ' è Maintenant nous allons avoir besoin d un moyen d indiquer au serveur ce param tre afin qu il é Il se trouve que nous nous abonnons d jà à la é 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 ' d articles partie int é grante du chemin , è nous donnant des URLs de la forme ' ' tre de limitation ' http://localhost:3000/25 . Un autre bon côté d utiliser l URL plutôt que d autres méthodes 25 est que si vous affichez vous verrez toujours 25 é . articles une fois la page charg Afin de faire ça proprement . abonnons aux articles , articles et que vous recharger la page dans le navigateur par erreur , e nous allons avoir besoin de changer la façon dont nous nous ' , Juste comme nous l avons fait dans le chapitre Commentaires é allons avoir besoin de d ' nous 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 : Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return [Meteor.subscribe('notifications')] } }); / . lib router js è Nous ajouterons un param è du param tre ' postsLimit tre signifie que c est optionnel . . au chemin de la route Ajouter un ? apr è s le nom Donc cette route ne correspondra pas seulement à http://localhost:3000/50 , mais également au simple et ancien http://localhost:3000 . //... Router.route('/:postsLimit?', { name: 'postsList', }); //... / . lib router js ' ' C est important de noter qu un chemin de la forme . chemins possibles Chaque route sera analys avec le chemin courant , ' En d autres mots , ' e successivement pour voir si elle correspond cificit é é d croissante . é les routes qui ciblent des routes plus sp viendraient en premi correspondra à tous les nous avons besoin de nous assurer que nous organisons nos routes é dans un ordre de sp é /:parameter? è , re et notre route postsList cifiques comme é serait d é plac /posts/:_id e en bas du groupe de . routes vu qu elle correspond pratiquement avec tout ' Il est maintenant temps d aborder le dur probl é . donn es é Nous avons besoin de g ' ù rer le cas o é donc nous l assignerons à une valeur par d ' è me de s abonner et trouver les bonnes è le param . faut tre postsLimit Nous utiliserons . “5” ' n est pas pr é , sent pour nous donner vraiment assez de place pour jouer avec les paginations //... Router.route('/:postsLimit?', { name: 'postsList', waitOn: function() { var limit = parseInt(this.params.postsLimit) || 5; return Meteor.subscribe('posts', {sort: {submitted: -1}, limit: limit}); } }); //... / . lib router js Vous noterez que nous passons maintenant un objet JavaScript }) postsLimit è param tre avec le nom de notre publication options ' pour l expression de ôé modifications dans notre code c t serveur pour impl é menter ça Meteor.publish('notifications', function() { return Notifications.find({userId: this.userId}); }); . { -} : 1, submitted : limit Posts.find() côté serveur. Faisons quelques Meteor.publish('comments', function(postId) { check(postId, String); return Comments.find({postId: postId}); }); / : sort posts . Cet objet servira au même titre que le Meteor.publish('posts', function(options) { check(options, { sort: Object, limit: Number }); return Posts.find({}, options); }); server publications js ({ : 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 postsLimit} ) par le client ' options pour servir comme les ( , {limit: dans notre cas 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 é est r ' , c est relativement anodin é ordonner les articles diff , remment ). mettre en place en premier lieu ' vu que tout ce qu un utilisateur peut faire ou changer la limite , en utilisant check() passer en douce des options suppl certains cas exposer des donn é Malgr , tout un pattern plus s - individuels eux m ô le contr mentaires es priv é é curis ' el devrait é es ). pourrait ( comme ê , mes au lieu de l objet entier é : ! é nous savons que les utilisateurs ne peuvent pas é é ê le de nos donn ce que nous voulons En revanche une appli du monde r probablement avoir besoin de limiter les limites Heureusement ( fields , qui pourrait dans tre de passer les param è tres pour nous assurer que nous gardons es Meteor.publish('posts', function(sort, limit) { return Posts.find({}, {sort: sort, limit: limit}); }); é Maintenant que nous nous sommes abonn mettre le contexte de donn é es au m ê . me endroit pattern et feront en sorte que la fonction . simplement retourner un curseur nous appellerons s au niveau de la route data ' é éé l , , é Nous d éé vierons un peu de notre pr c dent Ceci nous laisse cr é é er un contexte de donn é, es nomm que posts . rieur du template ment cela aurait du sens de retourne un objet JavaScript au lieu de ' 'ê Ce que cela signifie est simplement qu au lieu d l int , é notre contexte de donn le code devrait ê tre familier : tre implicitement disponible comme es sera disponible à this posts . A part ce petit à //... Router.route('/:postsLimit?', { name: 'postsList', 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}) }; } }); //... / . lib router js é Et puisque que nous avons mis le contexte de donn é maintenant nous d posts_list.js é é é notre contexte de donn es , capitulons ( posts é ê le m me pas besoin de toucher au template Voici à quoi notre nouveau code am posts , nous pouvons dans le fichier . ê nous n avons m R me du template helper et supprimer le contenu de ce fichier Nous avons nomm ' è barrasser sans probl es au niveau des routes é lior postsList de ), me nom que le helper ! router.js ressemble : donc Router.configure({ layoutTemplate: 'layout', loadingTemplate: 'loading', notFoundTemplate: 'notFound', waitOn: function() { return [Meteor.subscribe('notifications')] } }); Router.route('/posts/:_id', { name: 'postPage', waitOn: function() { return Meteor.subscribe('comments', this.params._id); }, data: function() { return Posts.findOne(this.params._id); } }); Router.route('/posts/:_id/edit', { name: 'postEdit', data: function() { return Posts.findOne(this.params._id); } }); Router.route('/submit', {name: 'postSubmit'}); Router.route('/:postsLimit?', { name: 'postsList', 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}) }; } }); var requireLogin = function() { if (! Meteor.user()) { if (Meteor.loggingIn()) { this.render(this.loadingTemplate); } else { this.render('accessDenied'); } } else { this.next(); } } Router.onBeforeAction('dataNotFound', {only: 'postPage'}); Router.onBeforeAction(requireLogin, {only: 'postSubmit'}); / . lib router js Commit 12-2 é Am ' Voir sur GitHub ' Lancer l instance è Essayons notre tout nouveau syst ' . liorer la route postsList pour avoir une limite . 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 verrez maintenant quelque chose comme ça : der à Contrôler le nombre d'articles sur la page d'accueil. http://localhost:3000/3 . Vous Pourquoi pas des pages ? - Pourquoi utilisons nous une approche de 10 pages successives avec é r sultats de recherche . ? ' “ pagination infinie , û articles sur chaque C est actuellement d ” au lieu de montrer des comme ce que Google fait pour ses -é au paradigme temps r é el embrass par Meteor Posts Imaginons que nous paginons notre collection , pagination de Google -- et que nous sommes à la page Que se passe t il si un utilisateur supprime un des é Étant donn . pourrait changer ' ' 11 ' 10 L article pendant que l article ' -é que notre application est temps r deviendrait l article 2, qui affiche les articles 10 é é pr , 9, el en utilisant le pattern de c dents articles ? 10 20. à é notre ensemble de donn es î et dispara trait de notre vue 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 traditionnelle est galement difficile à impl Revenons à notre exemple pr collection ' é rions cette bizarrerie dans l exp éé c . dent é ' l ensemble de donn Nous publions les articles ? es c t 10 é ' vous avez plus d un abonnement aux articles , et ensuite é. s -- Mais que va t il se passer si ô comme nous le ferons bient 10 20, é ôé . Disons qu un abonnement demande les articles articles charg , rer tous les articles publi . 20 Vous ne pouvez articles sur le serveur Ceci fonctionne si vous avez seulement un abonnement Vous avez maintenant de la client Posts.find() côté client pour récup ' à comme il y a seulement dix articles en tout dans Une solution serait simplement de publier ces 40. 10 20 10 20, ôé . é la pagination . à , menter pour des raisons techniques Posts , mais comment trouver ces articles sur le client pas prendre les articles de faire un rience utilisateur s c t à t ? et un autre les articles , client au total , ' ' la pagination traditionnelle n a pas beaucoup de sens . lorsqu on travaille avec Meteor Créer un contrôleur de route à avec aucun moyen de savoir lesquels appartiennent à quel abonnement Pour toutes ces raisons 30 é Vous avez not || 5; que nous r . deux fois , fin du monde Yourself : , De plus é é p tons la ligne var limit = parseInt(this.params.postsLimit) coder en dur le nombre ' “5” ' é . n est pas vraiment id mais comme c est toujours mieux de suivre le principe DRY Ne vous r é é petit peu les choses é Nous allons pr p . ) tez pas , si vous le pouvez senter un nouvel aspect de Iron Router , é utilisable dont toutes les routes peuvent h , uniquement pour une seule route é fonctionnalit ( ’ Don t Repeat ô les Contr é é riter . ' Maintenant nous allons l utiliser . deviendra pratique Router.route('/:postsLimit?', { name: 'postsList' }); . leur mais vous verrez dans le prochain chapitre comment cette //... / ô Un contr s de routage ensemble dans PostsListController = RouteController.extend({ template: 'postsList', increment: 5, postsLimit: function() { return parseInt(this.params.postsLimit) || this.increment; }, findOptions: function() { return {sort: {submitted: -1}, limit: this.postsLimit()}; }, waitOn: function() { return Meteor.subscribe('posts', this.findOptions()); }, data: function() { return {posts: Posts.find({}, this.findOptions())}; } }); lib router js . leurs de Route //... //... ' Ce n est pas la voyons comment nous pouvons remanier un de route est simplement un moyen de grouper les fonctionnalit un paquet r al . Allons y pas à pas Premi è , rement nous cr é ons notre contr ô leur en RouteController . Puis Nous mettons la propriété template , avant é é tape suppl è, s ' juste comme nous l avons fait t finissions une nouvelle fonction , mentaire ô waitOn et data ' ). s en occupe ' data de la d é ô leur . ô Si nous avions besoin d utiliser un contr controller ( que postsList , Iron et notre route Nous avons donc seulement besoin de finition de notre route ' é except findOptions . é PostsListController et pu utiliser l option , juste comme avant leur est appel Router utilisera automatiquement le contr waitOn Cela pourrait ressembler à une . maintenant elles vont utiliser notre nouvelle fonction supprimer et mais nous en ferons usage plus tard finissions des fonctions Puisque notre contr qui retournera la limite courante qui retournera un objet options é nous d , postslimit . findOptions une fonction Apr tendant é é increment . et ensuite une nouvelle propri Puis nous d é é ( é puisque dor ô navant le contr leur avec un autre nom , leur nous aurions ). nous verrons un exemple de ça dans le prochain chapitre Commit 12-3 é Route postsList remani es dans un contr Voir sur GitHub ô leur de route . ' Lancer l instance Ajouter un lien « Charger plus » Nous avons une pagination qui fonctionne ' ' , . et notre code est bien fait é il n y a aucun moyen d utiliser actuellement cette pagination except manuellement . Ceci ne fait d é é finitivement pas une bonne exp è Il y a juste un probl ' me en changeant l URL rience utilisateur , donc . retournons au travail pour corriger ça . Ce que nous voulons faire est assez simple ' , bas de notre liste d articles ' . qu on clique dessus cliquer sur “ é qui incr Nous allons ajouter un bouton ' ' Donc si je suis actuellement sur l URL ” Charger plus è nous am é mentera le nombre d articles affich ne à “ Charger plus s par 5 ” en chaque fois http://localhost:3000/5 , http://localhost:3000/10 . Si vous êtes arrivé : , aussi loin dans le livre éé Comme pr c ' nous pensons que vous pouvez supporter un peu d arithm demment , . tique nous ajouterons notre logique de pagination dans notre route vous souvenez quand nous avons explicitement nomm juste utiliser un curseur anonyme , seulement passer des curseurs de notre bouton é “ ”. ? , Bien ' é il n y a pas de r é notre contexte de donn gle qui dit que la fonction ê n ô data é é me technique pour g Vous es plut è donc nous utiliserons la m . ' t que peut rer l URL charger plus //... PostsListController = RouteController.extend({ template: 'postsList', increment: 5, postsLimit: function() { return parseInt(this.params.postsLimit) || this.increment; }, findOptions: function() { return {sort: {submitted: -1}, limit: this.postsLimit()}; }, waitOn: function() { return Meteor.subscribe('posts', this.findOptions()); }, posts: function() { return Posts.find({}, this.findOptions()); }, data: function() { var hasMore = this.posts().count() === this.postsLimit(); var nextPath = this.route.path({postsLimit: this.postsLimit() + this.inc rement}); return { posts: this.posts(), nextPath: hasMore ? nextPath : null }; } }); //... / . lib router js Regardons plus en d ( é qui h è tre . tail la magie du routeur ô rite du contr param é leur postsLimit . Vous vous souvenez que la route PostsListController sur lequel nous travaillons ) postsList prend un this.route.path() Donc quand nous alimentons {postsLimit: this.postslimit() avec + this.increment} , nous disons à la route postsList de construire son propre chemin en é . utilisant cet objet JavaScript comme contexte de donn ' En d autres mots , ' ê c est exactement la m es ' 'postsList'}} , excepté que nous remplaçons le this é donn {{pathFor me chose qu utiliser le helper Spacebars implicite par notre contexte de é. es personnalis ' é Nous prenons ce chemin et l ajoutons au contexte de donn ' ' . seulement s il y a plus d articles à afficher this.limit() Nous savons que , montrer qui peut ' mais é . e , . é ou notre valeur par d faut (5) ' si l URL ne tre ô é, this.posts é è ' ' tre la valeur dans l URL courante è ' re dont on fait ça est un peu rus , retourne le nombre courant d articles que nous aimerions ê contient pas de param D un autre c è La mani es pour notre template t r f , re au curseur courant donc this.posts.count() réfère au . nombre d articles qui sont actuellement dans le curseur n Donc ce que nous disons ici est que si nous demandons ' nous continuerons d afficher le bouton é nous r é cup rons moins de ê voulons arr Ceci é ' , de donn notre syst é è me es est exactement ”. charger plus é é cup n Mais si nous demandons n, rons et que n , ça voudra dire que nous avons atteint la limite et que nous ter d afficher ce bouton tant dit “ articles et nous r é . choue dans un cas ; n . Si cela arrive, le client demandera n articles et continuera à afficher le bouton . “ ' quand le nombre d items dans notre base ”, charger plus é articles et r ' ' é cup n rera ' inconscient qu il n y a plus d items restants ' C est triste , ' è il n y a pas de contournements simples à ce probl devrons nous contenter de cette impl ' ' ' donc pour l instant nous . mentation moins que parfaite Tout ce qu il reste à faire est d ajouter le lien ' - - é , me “ ” charger plus ' , en bas de notre liste d articles nous assurant de l afficher seulement si nous avons encore des articles à charger : en <template name="postsList"> <div class="posts"> {{#each posts}} {{> postItem}} {{/each}} {{#if nextPath}} <a class="load-more" href="{{nextPath}}">Load more</a> {{/if}} </div> </template> / / / _ client templates posts posts . list html ' Voici à quoi votre liste d articles devrait ressembler : Le bouton “Charger plus“. () nextPath é ajout Commit 12-4 ô au contr Voir sur GitHub é leur et utilis ' pour franchir Lancer l instance … Une meilleure expérience utilisateur , Notre pagination fonctionne maintenant correctement bizarrerie ' , d articles ' : à chaque fois que nous cliquons sur é nous sommes envoy é l arriv e des nouvelles donn « charger plus é . es Le r » s vers le template ' mais elle souffre d une ennuyeuse loading et que le routeur demande plus pendant que nous attendons é 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 è Donc premi tout . , rement , Au lieu de ça waitOn nous devrons dire à Iron Router de ne pas nous d é finirons nos abonnements dans un hook . Notez que nous ne renvoyons pas cet abonnement dans le hook normalement avec le hook ' subscription c est exactement ce que nous voulons subscriptions subscriptions . ( Le renvoyer ce qui est fait . viter , clencherais le hook de chargement global À la place é , et nous utilisons simplement le hook , finir notre abonnement de la m ê me onBeforeAction . Nous passons aussi une variable éé ' è é d comme un endroit pratique pour d façon que pour le hook l é ) ' l abonnement apr ready ment de notre contexte de donn l abonnement au post a termin é é . es qui se r éè f re à this.postsSub.ready comme un Cela nous permettra de dire au template quand de charger . s //... PostsListController = RouteController.extend({ template: 'postsList', increment: 5, postsLimit: function() { return parseInt(this.params.postsLimit) || this.increment; }, findOptions: function() { return {sort: {submitted: -1}, limit: this.postsLimit()}; }, subscriptions: function() { this.postsSub = Meteor.subscribe('posts', this.findOptions()); }, posts: function() { return Posts.find({}, this.findOptions()); }, data: function() { var hasMore = this.posts().count() === this.postsLimit(); var nextPath = this.route.path({postsLimit: this.postsLimit() + this.inc rement}); return { posts: this.posts(), ready: this.postsSub.ready, nextPath: hasMore ? nextPath : null }; } }); //... / . lib router js é Nous allons ensuite v rifier dans le template que cette variable ready affiche un spinner en bas de la liste de posts pendant que nous chargeons un nouveau lot de posts : <template name="postsList"> <div class="posts"> {{#each posts}} {{> postItem}} {{/each}} {{#if nextPath}} <a class="load-more" href="{{nextPath}}">Load more</a> {{else}} {{#unless ready}} {{> spinner}} {{/unless}} {{/if}} </div> </template> / / / _ client templates posts posts . list html Commit 12-5 é Avec un spinner pour rendre la pagination plus agr . able ' Voir sur GitHub Lancer l instance Accéder à des articles Nous sommes en train de charger les cinq articles les plus r -- ' ' é é cents par d passe t il quand quelqu un explore une page d article individuelle ? , faut mais que se Un template vide. , Si vous essayez vous allez faire face à une erreur ' « avons dit au routeur de s abonner à la publication » é . non trouv posts Cela semble normal : nous 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 -- é Comment demande t on au serveur pour un seul article sp un petit secret ici : ' Nous allons vous donner ! , é nous allons simplement cr er une nouvelle singlePost séparée qui publie seulement un article, identifié par son _id . Meteor.publish('posts', function(options) { return Posts.find({}, options); }); Meteor.publish('singlePost', function(id) { check(id, String) return Posts.find(id); }); //... ? derniers articles vous pouvez avoir plus d une publication pour chaque collection Donc pour retrouver nos articles manquants publication cifique . n / . server publications js Maintenant , - ôé abonnons nous aux bons articles c la publication comments ' sur la fonction simplement ajouter l abonnement à notre abonnement à la route t waitOn singlePost ' . client é Nous nous sommes d de la route . ici ' é jà abonn postPage , donc nous pouvons ' Et n oublions pas d ajouter é galement postEdit , vu qu elle nécessite aussi les mêmes données : //... Router.route('/posts/:_id', { name: 'postPage', waitOn: function() { return [ Meteor.subscribe('singlePost', this.params._id), Meteor.subscribe('comments', this.params._id) ]; }, data: function() { return Posts.findOne(this.params._id); } }); Router.route('/posts/:_id/edit', { name: 'postEdit', waitOn: function() { return Meteor.subscribe('singlePost', this.params._id); }, data: function() { return Posts.findOne(this.params._id); } }); //... / . lib router js Commit 12-6 … Utiliser un abonnement à un unique article pour nous assu Voir sur GitHub Avec la pagination activ é , e s à ' Lancer l instance notre application ne souffre plus de probl è é mes de mont e en , charge - û et les utilisateurs sont s ' rs de contribuer avec m è serait il pas super de pouvoir d une mani , saviez pas ' é é c est pr cis ' ' ê ' ? re ou d une autre classer ces liens . ment l objet du chapitre suivant . me plus de liens qu avant Donc ne Si vous ne le Le Vote 13 , Maintenant que notre site est en train de devenir populaire rapidement devenir d é licat . trouver les meilleurs liens va è Ce que nous avons besoin est un syst me de classement pour . ordonner nos articles Nous pourrions construire un syst , perte de points dans le temps , Telescope è me de classement complexe avec karma è le grand fr ' ). et plein d autres choses re de Microscope ( , é la plupart sont impl , Mais pour notre application é bas sur une é ment s dans nous garderons les ' . choses simples et nous noterons juste les articles par le nombre de votes qu ils ont reçus . Commençons par donner aux utilisateurs un moyen de voter sur les articles Modèle de données Nous stockerons une liste de votants sur chaque article afin de savoir si on doit montrer le ê bouton de vote aux utilisateurs et pour emp . cher les personnes de voter deux fois Confidentialité des données et Publications , Nous publierons ces listes de votants à tous les utilisateurs automatiquement ces donn ce qui rendra é es accessibles publiquement via la console du . navigateur ' C est le type de probl è é é me sur la confidentialit . façon dont les collections fonctionnent des donn Par exemple é personnes soient capables de trouver qui a vot , es qui peut subvenir de la - voulons nous que les pour leurs articles ' rendre cette information disponible publiquement n aura pas r é cons , quences Nous allons é facile la r upvoters é é , Dans notre cas ellement de mais il est important de reconna tre au minimum le probl è . me é galement d é cup et î ? normaliser le nombre total de votants sur un article pour rendre plus ration de ce chiffre . , Donc nous ajouterons deux attributs à nos articles votes . Commençons par les ajouter à notre fichier de pré - installation : // Données de préinstallation if (Posts.find().count() === 0) { var now = new Date().getTime(); // Créer deux utilisateurs var tomId = Meteor.users.insert({ profile: { name: 'Tom Coleman' } }); var tom = Meteor.users.findOne(tomId); var sachaId = Meteor.users.insert({ profile: { name: 'Sacha Greif' } }); var sacha = Meteor.users.findOne(sachaId); var telescopeId = Posts.insert({ title: 'Introducing Telescope', userId: sacha._id, author: sacha.profile.name, url: 'http://sachagreif.com/introducing-telescope/', submitted: new Date(now - 7 * 3600 * 1000), commentsCount: 2, upvoters: [], votes: 0 }); Comments.insert({ postId: telescopeId, userId: tom._id, author: tom.profile.name, submitted: new Date(now - 5 * 3600 * 1000), body: "C'est un projet intéressant Sacha, est-ce-que je peux y participe r ?" }); Comments.insert({ postId: telescopeId, userId: sacha._id, author: sacha.profile.name, submitted: new Date(now - 3 * 3600 * 1000), body: 'Bien sûr Tom !' }); Posts.insert({ title: 'Meteor', userId: tom._id, author: tom.profile.name, url: 'http://meteor.com', submitted: new Date(now - 10 * 3600 * 1000), commentsCount: 0, upvoters: [], votes: 0 }); Posts.insert({ title: 'The Meteor Book', userId: tom._id, author: tom.profile.name, url: 'http://themeteorbook.com', submitted: new Date(now - 12 * 3600 * 1000), commentsCount: 0, upvoters: [], votes: 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: new Date(now - i * 3600 * 1000 + 1), commentsCount: 0, upvoters: [], votes: 0 }); } / . server fixtures js ' , Comme d habitude , application éé propri t et cr ê arr é ez un nouvel utilisateur é s sont initialis , tez votre application . é ex meteor reset , redémarrez votre cutez - Assurons nous éé : es quand les articles sont cr s é galement ensuite que ces deux //... var postWithSameLink = Posts.findOne({url: postAttributes.url}); if (postWithSameLink) { return { postExists: true, _id: postWithSameLink._id } } var user = Meteor.user(); var post = _.extend(postAttributes, { userId: user._id, author: user.username, submitted: new Date(), commentsCount: 0, upvoters: [], votes: 0 }); var postId = Posts.insert(post); return { _id: postId }; //... / . collections posts js Templates de vote è Premi , rement nous allons ajouter un bouton de vote à notre article partiel et afficher le nombre de votes dans les m é tadonn é ' es de l article : <template name="postItem"> <div class="post"> <a href="#" class="upvote btn btn-default"> </a> <div class="post-content"> <h3><a href="{{url}}">{{title}}</a><span>{{domain}}</span></h3> <p> {{votes}} Votes, submitted by {{author}}, <a href="{{pathFor 'postPage'}}">{{commentsCount}} comments</a> {{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}} </p> </div> <a href="{{pathFor 'postPage'}}" class="discuss btn btn-default">Discuss </a> </div> </template> ⬆ / / / _ client templates posts post . item html Le bouton de vote , Ensuite bouton : é nous allons appeler une m ' thode serveur upvote quand l utilisateur clique sur le //... Template.postItem.events({ 'click .upvote': function(e) { e.preventDefault(); Meteor.call('upvote', this._id); } }); / / / _ client templates posts post , Finalement nous allons revenir à notre fichier é m . item js ôé thode Meteor c t lib/collections/posts.js serveur qui votera pour les articles et ajouter une : //... Meteor.methods({ post: function(postAttributes) { //... }, upvote: function(postId) { check(this.userId, String); check(postId, String); var post = Posts.findOne(postId); if (!post) throw new Meteor.Error('invalid', 'Post not found'); if (_.include(post.upvoters, this.userId)) throw new Meteor.Error('invalid', 'Already upvoted this post'); } }); Posts.update(post._id, { $addToSet: {upvoters: this.userId}, $inc: {votes: 1} }); //... / / . lib collections posts js Commit 13-1 Algorithme basique de vote ' Voir sur GitHub é Cette m . thode est assez directe ' ' é ' doublement que l utilisateur n a pas d incr 'é L é Lancer l instance é Nous faisons quelques v assurer que l utilisateur est authentifi . ' é rifications de s é et que l article existe r é é jà vot , pour cet article ' . ellement ' ' é curit pour nous Puis nous v é et si c est le cas nous mentons le score total de vote et ajoutons l utilisateur à l ensemble des votants é tape finale est int , ressante é comme nous avons utilis , en a beaucoup plus à apprendre ajoute un item à une propri éé t ' ' é deux op ê mais ces deux sont extr . é rateurs Mongo sp mement pratique é , de tableau tant qu elle n existe pas d . rifions jà et . ciaux Il y : $addToSet $inc incr é mente simplement un entier Optimisations de l'interface utilisateur ' ' é, Si l utilisateur n est pas authentifi é Pour refl , ter ça dans notre UI une classe CSS disabled é ou a d é jà vot , un article é il ne sera pas autoris nous utiliserons un helper pour ajouter de façon conditionnelle au bouton de vote . <template name="postItem"> <div class="post"> <a href="#" class="upvote btn {{upvotedClass}}"> </a> <div class="post-content"> //... </div> </template> ⬆ / / / _ client templates posts post . à voter . item html Template.postItem.helpers({ ownPost: function() { //... }, domain: function() { //... }, upvotedClass: function() { var userId = Meteor.userId(); if (userId && !_.include(this.upvoters, userId)) { return 'btn-primary upvotable'; } else { return 'disabled'; } } }); Template.postItem.events({ 'click .upvotable': function(e) { e.preventDefault(); Meteor.call('upvote', this._id); } }); / / / _ client templates posts post . item js Nous changeons notre classe 'é é l v nement click é galement . .upvote en ' .upvotable , donc n oubliez pas de changer Griser les boutons vote. Commit 13-2 é Griser lien vote quand non authentifi , é d Lancer l instance vous pouvez noter que les articles avec un seul vote sont . prenons le temps de pluraliser ces labels proprement é, compliqu ' é. jà vot ' Voir sur GitHub Ensuite / ' é Pluralisation peut s ê é é n . ' ù: ral que nous pourrons utiliser n importe o Template.registerHelper('pluralize', function(n, thing) { // pluraliser assez simpliste if (n === 1) { return '1 ' + thing; } else { return n + ' ' + thing + 's'; } }); ”, votes donc tre un processus mais pour l instant nous ferons ça d une façon assez simpliste un helper Spacebars g “ é 1 tiquet é Nous allons cr er / / . client helpers spacebars js Les helpers que nous avons cr ' . s appliquent qui peut éé Mais en utilisant ê é tre utilis ' à l int s avant ont éé t é reli au manager et template auxquels ils Template.registerHelper , nous avons créé un helper ' é rieur d un template global : <template name="postItem"> //... <p> {{pluralize votes "Vote"}}, submitted by {{author}}, <a href="{{pathFor 'postPage'}}">{{pluralize commentsCount "comment"}}</a> {{#if ownPost}}<a href="{{pathFor 'postEdit'}}">Edit</a>{{/if}} </p> //... </template> / / / _ client templates posts post . item html Perfectionner Pluralisation Propre (maintenant dites ça 10 fois) Commit 13-3 é Helper pluraliser ajout pour un meilleur format texte ' Voir sur GitHub “1 Maintenant vous devriez voir . Lancer l instance ”. vote Algorithme de vote plus intelligent Notre code de vote est bon , é nous cr ons deux appels vers Mongo è Il y a deux probl é donn ' . mes avec ça . es deux fois 1. é R é cup 3. ' un pour trouver l article è , rement , ' , ' Dans la m . ' ' c est un peu inefficace d aller vers la base de . Nous suivons : ' é . es é. rifier si l utilisateur a vot , Sinon ' faire un vote par l utilisateur -- . ' ê me utilisateur vote plusieurs fois pour l article entre les Notre code actuel ouvre la porte au utilisateur capables de voter pour le m . , thode upvote Que se passe t il si le m fois é l autre pour le mettre à jour il introduit une concurrence rer l article de la base de donn 2. é V Premi : Mais plus important l algorithme suivant . mais nous pouvons encore faire mieux Heureusement , 'ê Mongo nous permet d une seule commande Mongo : é tapes ê 1 et 3 ? me article deux tre plus intelligent et combine les é tapes - 13 dans //... Meteor.methods({ post: function(postAttributes) { //... }, upvote: function(postId) { check(this.userId, String); check(postId, String); var affected = Posts.update({ _id: postId, upvoters: {$ne: this.userId} }, { $addToSet: {upvoters: this.userId}, $inc: {votes: 1} }); if (! affected) throw new Meteor.Error('invalid', "Vous n'avez pas pu voter pour ce post ."); } }); //... / . collections posts js Commit 13-4 Meilleur algorithme de vote ' Voir sur GitHub ' cet utilisateur n a pas d é, vot é jà vot é, ' Lancer l instance ' “ Ce que nous sommes en train de dire c est trouve tous les articles avec cet ê ”. ' et mets les à jour dans ce sens il trouvera bien entendu l article avec cet la requ . te correspondra à aucun documents , ' id pour lesquels ' Si l utilisateur n a pas d ' id . D un autre côté si l utilisateur é et par cons é, a vot quent rien ne se passera é . jà alors Compensation de la latence é Disons que vous avez essay de tricher et envoy é un de vos articles en haut de la lise : en bidouillant son nombre de votes > Posts.update(postId, {$set: {votes: 10000}}); Console du navigateur ( ù postId O ' ' ) est l id d un de vos articles è Cette tentative impudente de jouer avec le syst deny() é . rejet ( é et imm diatement e . en action ê Ça peut ' - é Que s est il pass sans incident . ? , é vous pourriez voir la compensation de latence ' é , , ment sur le serveur é sultat final : é une erreur , en local ' a éé t éé é t appliqu é. rejet ponde . pas aider mais fait confiance à la collection en local ' , e . Puis un peu plus tard ), le è . en disant à la collection en local de revenir en arri é , a cutez Meteor sur votre propre machine en attendant que le serveur r refuse la modification update le te de la liste donc l article a atteint le haut de la liste update le ê vement en t , Posts Dans votre collection en millisecondes si vous ex serveur a retourn è mais l article bondira bri Ça arrive instantan Pendant ce temps mesur , . tre rapide avant de revenir à sa position é e par notre callback ?) , Le r r collections/posts.js , vous vous souvenez dans Mais si vous regardez attentivement ( éé me serait g ' re l interface utilisateur ne peut ô Aussit t que le serveur revient et les interfaces utilisateur s adaptent pour refl é . ter ça Classer les articles de la première page é Maintenant que nous avons un score pour chaque article bas . affichons la liste des meilleurs articles é souscriptions s é é . plus g n ral par é , Pour faire ça es sur la collection article , , sur le nombre de votes é nous allons voir comment g et rendre notre template rer deux postList un peu Pour commencer ' , , . nous voulons avoir deux souscriptions une pour chaque ordre de tri ê L astuce ici est que les deux souscriptions souscriront à la m é seulement avec des arguments diff Nous allons é galement cr rents pagination bien sur , Pour faire cela nous é tendrons notre NewPostListController ê exactement les m donnant un seul é er deux nouvelles routes appel ). et /new et /best ( es avec PostsListController BestPostsListController newPosts /new/5 NewPostsListController é de Iron Router éé Remplaçons donc la propri t de tri . 'ù d o é h riter {submitted: -1} . . distincts this.sort , qui sera fournie par NewPostsListController bestPosts , /best/5 , pour notre ô leurs é Ça nous laissera r home De plus dans et et dans deux contr mes options de routes pour les deux routes illustration de la flexibilit posts , . é accessibles respectivement aux URLs me publication ' et newPosts , en nous c est juste une belle PostsListController et utiliser par BestPostsListController : //... PostsListController = RouteController.extend({ template: 'postsList', increment: 5, postsLimit: function() { return parseInt(this.params.postsLimit) || this.increment; }, findOptions: function() { return {sort: this.sort, limit: this.postsLimit()}; }, subscriptions: function() { this.postsSub = Meteor.subscribe('posts', this.findOptions()); }, posts: function() { return Posts.find({}, this.findOptions()); }, data: function() { var hasMore = this.posts().count() === this.postsLimit(); return { posts: this.posts(), ready: this.postsSub.ready, nextPath: hasMore ? this.nextPath() : null }; } }); NewPostsController = PostsListController.extend({ sort: {submitted: -1, _id: -1}, nextPath: function() { return Router.routes.newPosts.path({postsLimit: this.postsLimit() + this .increment}) } }); BestPostsController = PostsListController.extend({ sort: {votes: -1, submitted: -1, _id: -1}, nextPath: function() { return Router.routes.bestPosts.path({postsLimit: this.postsLimit() + thi s.increment}) } }); Router.route('/', { name: 'home', controller: NewPostsController }); Router.route('/new/:postsLimit?', {name: 'newPosts'}); Router.route('/best/:postsLimit?', {name: 'bestPosts'}); / . lib router js ' Notez que maintenant que nous avons plus d une route hors de PostListController , nous prenons la logique NewPostsController et la plaçons dans nextPath et BestPostsController , à partir du moment où le chemin sera différent dans les deux cas. , De plus puis par quand nous trions par _id ' è pour nous assurer que l ordre est enti ô Avec nos nouveaux contr é d barrasser de la route leurs en place postList , éé pr c . dente è me nous Supprimez simplement le code suivant ' -ê galement des liens dans l en t : . é é. cifi nous pouvons maintenant sans probl lib router js Nous ajouterons é rement sp Router.route('/:postsLimit?', { name: 'postsList' }) / ' votes , nous avons des tris ultérieurs par horodatage d envoi te : <template name="header"> <nav class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="col lapse" data-target="#navigation"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="{{pathFor 'home'}}">Microscope</a> </div> <div class="collapse navbar-collapse" id="navigation"> <ul class="nav navbar-nav"> <li> <a href="{{pathFor 'newPosts'}}">New</a> </li> <li> <a href="{{pathFor 'bestPosts'}}">Best</a> </li> {{#if currentUser}} <li> <a href="{{pathFor 'postSubmit'}}">Submit Post</a> </li> <li class="dropdown"> {{> notifications}} </li> {{/if}} </ul> <ul class="nav navbar-nav navbar-right"> {{> loginButtons}} </ul> </div> </nav> </template> / / / . client templates includes header html Et finalement , nous avons ' suppression d article : é galement besoin de mettre à jour notre gestionnaire de 'click .delete': function(e) { e.preventDefault(); } / if (confirm("Delete this post?")) { var currentPostId = this._id; Posts.remove(currentPostId); Router.go('home'); } / / _ client templates posts posts Avec tout cela de fait , . edit js . nous avons maintenant une liste des meilleurs posts Classer par points Routes ajout é Commit 13-5 ' , es pour les listes d articles Voir sur GitHub ' … et pages pour Lancer l instance Une meilleur en-tête ' , Maintenant que nous avons deux pages de liste d articles laquelle vous é . vident ê tes en train de regarder Nous allons cr . ê é er un gestionnaire é header.js et cr é tre difficile de savoir -ê Donc revisitons notre en t courant et une ou plusieurs routes nomm navigation il peut te pour rendre ça plus er un helper qui utilise le chemin es pour ajouter une class active dans nos items de : é La raison pour laquelle nous voulons supporter de multiples routes nomm routes ê le m home et newPosts me template . ( qui correspondent respectivement aux URLs Ça signifie que notre pour rendre actif le tag <li> activeRouteClass . dans les deux cas devrait ê / es est que les deux et /new ) invoque tre assez intelligente <template name="header"> <nav class="navbar navbar-default" role="navigation"> <div class="navbar-header"> <button type="button" class="navbar-toggle collapsed" data-toggle="col lapse" data-target="#navigation"> <span class="sr-only">Toggle navigation</span> <span class="icon-bar"></span> <span class="icon-bar"></span> <span class="icon-bar"></span> </button> <a class="navbar-brand" href="{{pathFor 'home'}}">Microscope</a> </div> <div class="collapse navbar-collapse" id="navigation"> <ul class="nav navbar-nav"> <li class="{{activeRouteClass 'home' 'newPosts'}}"> <a href="{{pathFor 'newPosts'}}">New</a> </li> <li class="{{activeRouteClass 'bestPosts'}}"> <a href="{{pathFor 'bestPosts'}}">Best</a> </li> {{#if currentUser}} <li class="{{activeRouteClass 'postSubmit'}}"> <a href="{{pathFor 'postSubmit'}}">Submit Post</a> </li> <li class="dropdown"> {{> notifications}} </li> {{/if}} </ul> <ul class="nav navbar-nav navbar-right"> {{> loginButtons}} </ul> </div> </nav> </template> / / / . client templates includes header html Template.header.helpers({ activeRouteClass: function(/* route names */) { var args = Array.prototype.slice.call(arguments, 0); args.pop(); var active = _.any(args, function(name) { return Router.current() && Router.current().route.getName() === name }); } }); / return active && 'active'; / / . client templates includes header js Montrer la page active Arguments des helpers ' é , Nous n avons pas utilis ' d autres tags Spacebars é ce patron sp ' , cifique jusqu à maintenant mais tout comme . les tags de template helper peuvent prendre des arguments é Et pendant que vous pouvez bien entendu passer des arguments nomm dans votre fonction , è param é é é vous pouvez tres anonymes et les r cup é galement passer un nombre non sp ' . é cifi arguments rer en appelant l objet é s sp cifiques de dans la fonction , Dans ce dernier cas tableau Javascript classique et ensuite appeler é du hash ajout , (. . i e , le helper Any() activeRouteClass de Underscore pour voir si certaines des routes leur url correspondante est é ). gale au chemin courant , any() , myString nous prenons avantage du patron Javascript retourne false , mais true && myString é Classes active ajout Voir sur GitHub ' -ê es à l en t te . ' Lancer l instance é , el vous verrez les . items monter et descendre la page d accueil en fonction de leur classement ' ù false && o myString . Maintenant que les utilisateurs peuvent voter sur les articles en temps r ' true . retournera boolean && string retourne Commit 13-6 ' barrasser prendre une liste de noms de Si une de ces routes correspond avec le chemin courant Finalement é dessus pour vous d en . et ensuite utilise le helper passent le test pop() arguments à la fin par Spacebars Pour chaque item de navigation routes ' vous voudrez probablement convertir l objet ? Mais ne serait il é pas joli s il y avait un moyen d affiner tout ça avec quelques animations bien temporis es Publications Avancées , À partir de maintenant 13.5 SIDEBAR abonnements interagissent . ’ e de comment les publications et les î é Donc passez le cap de l entra nement et examinez des sc é. un peu plus avanc é vous devriez avoir une bonne id narios s Publier une collection de multiples fois è Dans notre premi , re annexe à propos des publications ' , classiques de publications et d abonnements _publishCursor è Premi , rement et nous avons appris comment la fonction é nous a rendu la tâche facile pour les impl rappelons ce que _publishCursor . me nom : é, ê . menter dans nos propres sites fait pour nous exactement les documents qui correspondent à un curseur donn client du m nous avons vu quelques patterns il prend tous ôé et les envoie dans la collection c ' Notez que le nom de la publication n est en aucun cas impliqu t é. ' ' Cela signifie que nous avons plus d une publication reliant les versions client et serveur d une . collection é Nous avons d é avons publi jà rencontr - é un sous ensemble pagin é ' é ' quand nous é. de tous les articles en plus de l article affich ' ' Un autre cas similaire est de publier une vue d ensemble d un large aussi bien que les d , ce pattern dans notre chapitre sur la pagination tails complets d un seul item : é , chantillons de documents Publier une collection deux fois Meteor.publish('allPosts', function() { return Posts.find({}, {fields: {title: true, author: true}}); }); Meteor.publish('postDetail', function(postId) { return Posts.find(postId); }); ' , Maintenant que le client s abonne à ces deux publications de deux sources ' : ' sa collection 'posts' , une liste de titres et de noms d auteur du premier abonnement è complets d un article provenant du deuxi . me abonnement est remplie é et les d tails ( ' é Vous pouvez r ' aliser que l article publi - é postDetail par est é ) éé . bien qu avec seulement un sous ensemble de ses propri ' t s é galement publi , Cependant ' ' par allPosts Meteor prend soin ' du chevauchement en rassemblant les champs et en s assurant qu il n y a pas d article é. dupliqu ' é C est g , nial parce que maintenant quand nous affichons la liste r nous avons affaire à des objets de donn nous avons besoin . Cependant , é , capitulative des articles é es qui ont juste assez de donn es pour afficher ce que quand nous affichons la page pour un seul article tout ce que nous avons besoin pour le montrer ' é . û, Bien s r , nous avons nous devons faire attention sur le client à ne pas s attendre à ce que tous les champs soient disponibles sur tous les articles dans ce cas –' c est un pi è ge habituel Il convient de noter que vous n è pouvez tr é diff s bien publier les m ! 'ê é tes pas limit ê éé mes propri t aux diff é éé rentes propri t . s des documents , s dans les deux publications Vous mais les trier . remment Meteor.publish('newPosts', function(limit) { return Posts.find({}, {sort: {submitted: -1}, limit: limit}); }); Meteor.publish('bestPosts', function(limit) { return Posts.find({}, {sort: {votes: -1, submitted: -1}, limit: limit}); }); / . server publications js S'abonner à une publication plusieurs fois ' . Nous venons juste de voir comment publier une collection plus d une fois pouvez accomplir le m ' ê é me r sultat avec un autre pattern : é cr ' Il s av è re que vous , er une seule publication mais . s y abonner plusieurs fois , Dans Microscope é configure et d nous nous abonnons à la publication truit chaque abonnement pour nous é puisse pas nous abonner plusieurs fois simultan . posts plusieurs fois ' , mais Iron Router ' Pourtant il n y a aucune raison qu on ne . ment , Par exemple é r disons que nous voulons charger en m é cents en m moire ê me temps les meilleurs articles et les plus : S'abonner deux fois à une publication Nous mettons en place une publication : Meteor.publish('posts', function(options) { return Posts.find({}, options); }); Et ensuite nous nous abonnons à cette publication plusieurs fois exactement ce que nous faisons dans Microscope : . ' En fait c est plus ou moins Meteor.subscribe('posts', {submitted: -1, limit: 10}); Meteor.subscribe('posts', {baseScore: -1, submitted: -1, limit: 10}); ' - ' Qu est ce qu il se passe exactement ? é Chaque navigateur ouvre deux abonnements diff ê chacun se connecte à la m me publication sur le serveur é Chaque abonnement fournit des arguments diff , fondamentalement é posts collection chaque fois un ensemble et envoy ê é le sera , rents à la publication é diff ) rent ù ! mais de documents est recueilli de la ôé t . client ê me abonnement avec les m Difficile d imaginer beaucoup de situations o flexibilit . dans le tuyau vers la collection c Vous pouvez aussi souscrire deux fois au m ' ( ' , rents , c est utile mais peut ê ! mes arguments ' tre qu un jour cette De multiples collections dans un seul abonnement Contrairement aux bases de donn é é bases de donn ô es NoSQL comme Mongo sont plut . é Nous avons ajout ’ é t dans la d Voyons comment ça fonctionne dans le contexte de Meteor Regardons un exemple concret , es traditionnelles comme MySQL qui utilise des joins les é. normalisation et l embarqu . , des commentaires à nos articles ' ' , et depuis nous sommes heureux de publier uniquement les commentaires de l article que l utilisateur . est en train de lire , Cependant è premi supposez que nous voulions montrer les commentaires de tous les articles de la re page ' ( garder en t classique d utilisation pr , articles ' ). ê te que ces articles changeront quand nous les paginerons é sente une bonne raison pour embarquer les commentaires dans les é et en fait c est ce qui nous pousse à d . normaliser les compteurs de commentaires û Bien s , r nous pouvons toujours juste embarquer les commentaires dans les articles é d é normalisation é avec des collections diff se Comments . Mais comme nous avons vu précédemment dans le barrasser de la collection chapitre de d Ce cas , é é en faisant ça nous perdrions certains des b . rentes n fices de travailler ' è Mais il s av ' ' re qu il y a une astuce impliquant les abonnements qui rende possible d inclure nos commentaires tout en pr ' é é servant des collections s Supposons qu avec notre premi ' è par , re page de liste d articles é . es nous voulons nous abonner à une . liste des deux top commentaires de chaque article ' é Il serait difficile d accomplir ça avec une publication de commentaires ind é sp ' cialement si la liste d articles Nous devrions é é é tait limit ' d une certaine façon crire une publication qui ressemblerait à ça Deux collections dans un abonnement : ( disons , , pendants les 10 plus r é ). cents Meteor.publish('topComments', function(topPostIds) { return Comments.find({postId: topPostIds}); }); è Ça causerait un probl é d é mont - me de performance éé e et r . , : topPostsIds . tre change Nous utilisons juste le fait que nous ne pouvons pas seulement avoir plus d une publication par collection par publication 'ê comme la publication aurait besoin d tablie à chaque fois que la liste de Il y a un moyen de faire ça ' , ' mais nous pouvons aussi avoir plus d une collection Meteor.publish('topPosts', function(limit) { var sub = this, commentHandles = [], postHandle = null; // Envoyer les deux top commentaires d'un post unique function publishPostComments(postId) { var commentsCursor = Comments.find({postId: postId}, {limit: 2}); commentHandles[postId] = Mongo.Collection._publishCursor(commentsCursor, sub, 'comments'); } postHandle = Posts.find({}, {limit: limit}).observeChanges({ added: function(id, post) { publishPostComments(postId); sub.added('posts', id, post); }, changed: function(id, fields) { sub.changed('posts', id, fields); }, removed: function(id) { // Stopper l'observation des changelents sur les commentaires de l'art icle commentHandles[id] && commentHandles[id].stop(); // Supprimer l'article sub.removed('posts', id); } }); sub.ready(); // S'assurer que nous nettoyons tout (notez que `_publishCursor` // fait ça pour nous avec les observateurs de commentaire) sub.onStop(function() { postHandle.stop(); }); }); , Notez que nous ne retournons rien dans cette publication ê m mes manuellement des messages à pas besoin de demander à Maintenant , sub _publishCursor ( via .added() et consorts é. automatiquement les deux top commentaires qui y sont attach ' ). ' Donc nous n avons . de faire ça pour nous en retournant un curseur chaque fois que nous publions un article nous publions appel d abonnement - comme nous envoyons nous ! è Bien que Meteor ne rend pas encore cette approche tr , s direct s é galement Et tout ça avec un seul vous pouvez voir chercher le paquet publish-with-relations plus facile à utiliser , sur Atmosphere qui a pour objectif de rendre ce pattern . Relier des collections différentes ' - é Qu est ce que notre nouvelle connaissance sur la flexibilit apporter ? , Bien ' si nous n utilisons pas des abonnements pourrait nous ' _publishCursor , nous n avons pas besoin de suivre la ' ê contrainte que la collection source sur le serveur a besoin d avoir le m . me nom que la collection cible sur le client Une collection pour deux abonnements Une raison de pourquoi nous voudrions faire ça est Single Table Inheritance (é h ritage de ). tableau unique Supposons que nous voulions r ces champs communs stock éé f é éé é è s mais aussi a diff nous pourrions construire un syst , classique ID timestamp et title juste du texte ; ' , rencer divers types d objets de nos articles r l g . rement dans le contenu è ù me de blog comme Tumblr o é mais en plus peut , Par exemple chaque article poss é galement g rer une image , è é , vid o de le , lien ou . 'resources' , en utilisant un Nous pouvons stocker tous ces objets dans une seule collection attribut dont chacun de type ' ( . video , image , link , pour indiquer quelle sorte d objet ils sont Et bien que nous avons une seule collection Resources sur le serveur transformer cette unique collection en multiples collections client avec ce petit tour de magie , ) .. etc nous pouvons Videos , Images , etc. sur le : Meteor.publish('videos', function() { var sub = this; var videosCursor = Resources.find({type: 'video'}); Mongo.Collection._publishCursor(videosCursor, sub, 'videos'); // _publishCursor ne fait pas l'appel pour nous au cas où nous voudrions le faire plusieurs fois. sub.ready(); }); Nous disons à , ferait _publishCursor é de publier nos vid ô mais plut t que de publier à la collection 'resources' publions de é Une autre id vers ' os ( resources , sur le client ôé t serveur ! ôé , Par exemple ôé tiers et les publier dans une collection c Grâce à la flexibilit é ' , de l API publish que le curseur à la place nous 'videos' . e similaire est d utiliser publish avec une collection c tout de collection c ) juste en retournant t vous pouvez r é é cup . é s sont infinies ù client o é rer les donn client les possibilit t . ' ' il n y a pas du es d un service Animations //// Meteor & the DOM //// //// //// //// //// 2. //// 3. //// 4. //// 5. //// 6. //// //// 1. 14 Swtiching two posts //// //// //// Proper Timing //// //// //// //// //// //// CSS Positioning //// //// //// //// //// .post{ position:relative; transition:all 300ms 0ms ease-in; } / / . client stylesheets style css //// //// Position:absolute //// //// Total Recall //// //// //// //// //// Ranking Posts //// //// //// //// Template.postsList.helpers({ postsWithRank: function() { this.posts.rewind(); return this.posts.map(function(post, index, cursor) { post._rank = index; return post; }); } }); / / / / _ client views posts posts . list js //// //// <template name="postsList"> <div class="posts"> {{#each postsWithRank}} {{> postItem}} {{/each}} {{#if nextPath}} <a class="load-more" href="{{nextPath}}">Load more</a> {{/if}} </div> </template> / / / / _ client views posts posts . list html Be Kind, Rewind //// //// //// Putting it together //// Template.postItem.helpers({ //... }); Template.postItem.rendered = function(){ // animate post from previous position to new position var instance = this; var rank = instance.data._rank; var $this = $(this.firstNode); var postHeight = 80; var newPosition = rank * postHeight; // if element has a currentPosition (i.e. it's not the first ever render) if (typeof(instance.currentPosition) !== 'undefined') { var previousPosition = instance.currentPosition; // calculate difference between old position and new position and send e lement there var delta = previousPosition - newPosition; $this.css("top", delta + "px"); } // let it draw in the old position, then.. Meteor.defer(function() { instance.currentPosition = newPosition; // bring element back to its new original position $this.css("top", "0px"); }); }; Template.postItem.events({ //... }); / / / / _ client views posts post . item js Commit 14-1 . Added post reordering animation Voir sur GitHub //// ' Lancer l instance //// //// Animating New Posts //// //// //// 2. //// //// 1. //// Template.postItem.helpers({ //... }); Template.postItem.rendered = function(){ // animate post from previous position to new position var instance = this; var rank = instance.data._rank; var $this = $(this.firstNode); var postHeight = 80; var newPosition = rank * postHeight; // if element has a currentPosition (i.e. it's not the first ever render) if (typeof(instance.currentPosition) !== 'undefined') { var previousPosition = instance.currentPosition; // calculate difference between old position and new position and send e lement there var delta = previousPosition - newPosition; $this.css("top", delta + "px"); } else { // it's the first ever render, so hide element $this.addClass("invisible"); } // let it draw in the old position, then.. Meteor.defer(function() { instance.currentPosition = newPosition; // bring element back to its new original position $this.css("top", "0px").removeClass("invisible"); }); }; Template.postItem.events({ //... }); / / / / _ client views posts post . item js Commit 14-2 . Fade items in when they are drawn Voir sur GitHub ' Lancer l instance //// CSS & JavaScript //// //// //// Vocabulaire 99 Client Lorsque nous parlons de client , nous faisons r éé f rence au code qui tourne dans le navigateur , web des utilisateurs ' , que ce soit un navigateur traditionnel comme Firefox ou Safari ou bien . quelque chose d aussi complexe que UIWebView dans une application native pour iPhone Collection ' ô Une collection Meteor est l entrep é synchronis entre le client et le serveur é é g n é t de donn . é datastore ) qui est automatiquement Les collections ont un nom . ê ralement sur le client et sur le serveur ont une API commune bas ( es ' M ( ) . posts , ex é me si elles se comportent diff et existent , remment elles . e sur l API de Mongo Computation “ Une computation ” les sources de donn versatiles é lui d ( ' est un bloc de code qui tourne à chaque fois qu il y a un changement dans é é es versatiles dont il d ) par exemple une variable de session finir une computation . pend . é Si vous avez une source de donn es et que vous voulez interagir avec elle , il faudra Curseur (Cursor) ' ê Un curseur est le produit d une requ ' ' curseur n est pas qu un tableau de r . te sur une collection Mongo é , sultats , Au niveau du client é mais un objet r actif qui peut é, lorsque des objets dans la collection en question sont ajout s enlev ê tre observ é un é é. s et modifi s DDP , DDP protocole de distribution de donn é synchroniser les collections et faire les appels de m é é protocole g é . donn es n , es de Meteor é é est le protocole utilis thodes . pour éê DDP est suppos tre un é rique qui prend la place du HTTP pour les applications en temps r el lourdes en Tracker Tracker est le syst è me r é . actif de Meteor é automatiquement synchronis Tracker agit dans les coulisses pour garder le HTML è avec le mod é le de donn . es choisi Document Mongo utilise les “ ” documents collections sont appel é s “ é ”. documents ) peuvent pas contenir de fonctions suivre les donn es sur DDP é pour stocker les donn . es et donc les objets appartenant aux ( Ce sont des objets JavaScript de base éé avec une propri t é sp ciale _id ê m ' me s ils ne qui permet à Meteor de Helpers é é Quand un template a besoin de g , document n ' éé rer quelque chose de plus complexe qu une propri il peut faire appel à un helper , t de . une fonction qui aide pour le rendu Compensation de latence (Latency compensation) ' C est une technique qui permet de simuler des appels de m é les d é lais en attentant la r é é thodes du cot client pour é viter . ponse du serveur Le groupe de développement de Meteor (Meteor Development Group, MDG) ' é L entreprise d veloppant actuellement Meteor , - par opposition avec le framework lui m ê . me Méthode (Method) é Une m é thode Meteor est un appel de proc é une logique sp Les m é thodes ont é ciale pour d tecter les changements dans les collections ainsi que des é m . dure du client vers le serveur é . canismes de compensation de d lai MiniMongo La collection cot é é client est stock e dans un datastore qui offre une API comparable à Mongo La librairie qui supporte cette API est appel é e “ ” MiniMongo ' pour indiquer que c est une . é version simplifi è e de Mongo qui op è re enti é rement dans la m . moire Paquet (Package) Un paquet Meteor peut JavaScript destin ( é ê é tre du code JavaScript destin ), comme par exemple SASS vers CSS Un paquet est comme une biblioth è liste compl , à tourner sur le client du code . ou des ressources à traiter . que surpuissante Non seulement Meteor est livr é avec une mais il y a aussi Atmosphere qui est une collection de te de paquets de base paquets offerts par la communaut , des instructions sur la gestion des ressources è , à tourner sur le serveur é. Publication Une publication est un jeu de donn ' . utilisateur qui s y abonne Vous cr é é es qui a un nom et qui est personnalis é pour chaque . ez une publication sur le serveur Serveur (Server) . . Le serveur Meteor est un serveur HTTP et DDP via Node js è biblioth Il est constitu ques Meteor ainsi que du code JavaScript que vous avez cr Quand le serveur Meteor d automatiquement ). é , marre éé é il se connecte à la base de donn é de toutes les é . du cot du serveur ( e Mongo é qui d marre Session é La session dans Meteor correspond à une source de donn ê 'é é tre utilis e par votre application pour garder l é es versatile du cot ' , client qui peut . tat dans lequel se trouve l utilisateur Abonnement (Subscription) . Un abonnement est une connexion à une publication pour un client particulier ’ est du code qui s ex é é cute dans le navigateur et qui garde les donn . publication du serveur Patron (Template) ' L abonnement é es synchronis es avec la ' é é Un patron permet de g syst n . rer de l HTML en Javascript è ( me de template sans logique - ), logic less é Par d qui pourrait ê , faut tre é , Meteor utilise Spacebars tendu dans le futur un . Contexte des données du patron (Template Data Context) ' é è Lorsqu un patron g é sp n , re un rendu , JavaScript objects peuvent ê éè f G ), POJOs tre beaucoup plus n é re à un objet JavaScript qui livre des donn . é é cifiques à ses besoins - il se r ralement ce sont de bons vieux objets JavaScript ( é labor é s et poss der leur propres fonctions . - ê ' plain old , souvent des documents appartenant à une collection é es m me s ils