Construire un gestionnaire de contacts en utilisant Backbone.js Partie 2

Bienvenue dans la deuxième partie de ce didacticiel. dans la première partie, nous avons examiné les bases du modèle, de la collection et de la vue pour l'utilisation de Backbone et avons vu comment restituer des vues de contact individuelles à l'aide d'une vue principale liée à une collection.

Dans cette partie du didacticiel, nous allons voir comment filtrer notre vue en fonction des entrées de l'utilisateur et comment ajouter un routeur pour donner à notre application de base une fonctionnalité d'URL..
Nous aurons besoin des fichiers source de la première partie, car nous allons nous appuyer sur le code existant pour cette partie. Je vous recommande fortement de lire la première partie si vous ne l'avez pas déjà.


Réagir à la saisie de l'utilisateur

Vous avez peut-être remarqué dans la première partie que chacun de nos modèles possède un type appelé attribut qui classe chaque modèle en fonction de son lien avec un ami, un membre de la famille d'un collègue. Ajoutons un élément select à notre vue principale pour permettre à l'utilisateur de filtrer les contacts en fonction de ces types..

Maintenant, nous pouvons coder un menu de sélection dans notre code HTML sous-jacent et ajouter manuellement des options pour chacun des différents types. Mais, cela ne serait pas très avant-gardiste; Et si nous ajoutions un nouveau type ultérieurement ou supprimions tous les contacts d'un certain type? Notre application n'a pas encore la capacité d'ajouter ou de supprimer des contacts (troisième partie alerte au spoiler!), Mais il est toujours préférable de prendre en compte ce genre de choses, même à ce stade précoce de notre application..

En tant que tel, nous pouvons facilement construire un élément de sélection de manière dynamique en fonction des types existants. Nous allons d’abord ajouter un petit peu de HTML à la page sous-jacente; ajoutez les nouveaux éléments suivants au conteneur de contacts:

 

Ça y est, nous avons un extérieur élément servant de conteneur général, dans lequel se trouve un autre conteneur avec une identifiant attribut, et un avec un texte explicatif.

Maintenant construisons le ", html:""); _.each (this.getTypes (), fonction (élément) var option = $ ("

La première de nos méthodes, getTypes () retourne un tableau créé avec Underscore uniq () méthode. Cette méthode accepte un tableau en tant qu'argument et renvoie un nouveau tableau contenant uniquement des éléments uniques. Le tableau que nous passons dans le uniq () la méthode est générée en utilisant le Backbone cueillir() méthode, qui est un moyen simple d'extraire toutes les valeurs d'un seul attribut d'une collection de modèles. L’attribut qui nous intéresse ici est le type attribut.

Afin d'éviter des problèmes de cas plus tard, nous devrions également normaliser les types en minuscules. Nous pouvons utiliser une fonction itérateur, fournie comme troisième argument de uniq (), pour transformer chaque valeur avant qu’elle soit passée au comparateur. La fonction reçoit l'élément actuel en tant qu'argument, nous renvoyons donc l'élément au format minuscule. Le deuxième argument passé à uniq (), que nous avons mis à faux ici, un drapeau est-il utilisé pour indiquer si le tableau comparé a été trié.

La deuxième méthode, createSelect () est légèrement plus grand, mais pas beaucoup plus complexe. Son seul but est de créer et de retourner un nouveau .

Pour rendre le élément, avec une option pour chacun des différents types de contact:


Filtrer la vue

Alors maintenant, nous avons notre menu, nous pouvons ajouter la fonctionnalité pour filtrer la vue quand une option est sélectionnée. Pour ce faire, nous pouvons utiliser la vue principale événements attribut pour ajouter un gestionnaire d’événements UI. Ajoutez le code suivant directement après notre renderSelect () méthode:

événements: "change #filter select": "setFilter",

le événements attribut accepte un objet de valeur clé paires où chaque clé spécifie le type d'événement et un sélecteur auquel lier le gestionnaire d'événements. Dans ce cas, nous nous intéressons à la changement événement qui sera tiré par le élément dans le #filtre récipient. Chaque valeur de l'objet est le gestionnaire d'événement qui devrait être lié. dans ce cas nous spécifions setFilter en tant que gestionnaire.

Ensuite, nous pouvons ajouter le nouveau gestionnaire:

setFilter: function (e) this.filterType = e.currentTarget.value; this.trigger ("change: filterType"); ,

Tout ce que nous devons faire dans le setFilter () fonction est définie une propriété sur la vue principale appelée filterType, que nous avons mis à la valeur de l'option qui a été sélectionnée, qui est disponible via le currentTarget propriété de l'objet événement qui est automatiquement passé à notre gestionnaire.

Une fois que la propriété a été ajoutée ou mise à jour, nous pouvons également déclencher une changement event for it en utilisant le nom de la propriété comme espace de noms. Nous verrons comment utiliser cet événement personnalisé dans un instant, mais avant cela, nous pouvons ajouter la fonction qui effectuera effectivement le filtre. après le setFilter () méthode ajouter le code suivant:

filterByType: function () if (this.filterType === "all") this.collection.reset (contacts);  else this.collection.reset (contacts, silent: true); var filterType = this.filterType, filtré = _.filter (this.collection.models, function (item) retour item.get ("type"). toLowerCase () === filterType;); this.collection.reset (filtré); 

Nous vérifions d’abord si la vue principale est filterType la propriété est définie sur tout; si c’est le cas, nous repeuplerons simplement la collection avec l’ensemble complet de modèles dont les données sont stockées localement sur notre ordinateur. Contacts tableau.

Si la propriété n'est pas égale tout, nous réinitialisons toujours la collection pour récupérer tous les contacts dans la collection, ce qui est nécessaire pour permuter entre les différents types de contact, mais cette fois, nous définissons la silencieux option de vrai (vous verrez pourquoi cela est nécessaire dans un instant) pour que le réinitialiser événement n'est pas déclenché.

Nous stockons ensuite une version locale de la vue filterType propriété afin que nous puissions la référencer dans une fonction de rappel. Nous utilisons Underscore filtre() méthode pour filtrer la collection de modèles. le filtre() La méthode accepte le tableau à filtrer et une fonction de rappel à exécuter pour chaque élément du tableau en cours de filtrage. La fonction de rappel est passé l'élément actuel en tant qu'argument.

La fonction de rappel retournera vrai pour chaque article qui a un type attribut égal à la valeur que nous venons de stocker dans la variable. Les types sont à nouveau convertis en minuscules, pour les mêmes raisons que précédemment. Tous les éléments renvoyés par la fonction de rappel faux pour sont supprimés du tableau.

Une fois le tableau filtré, nous appelons le réinitialiser() méthode encore une fois, en passant dans le tableau filtré. Nous sommes maintenant prêts à ajouter le code qui reliera le setType () méthode, la filterType propriété et filterByType () méthode.


Reliure d'événements à la collection

En plus de lier des événements d'interface utilisateur à notre interface à l'aide du événements attribut, nous pouvons également lier les gestionnaires d’événements aux collections. Dans notre setFilter () méthode, nous avons déclenché un événement personnalisé, nous devons maintenant ajouter le code qui liera le filterByType () méthode pour cet événement; ajoutez le code suivant à la initialiser() méthode de notre vue principale:

this.on ("change: filterType", this.filterByType, this);

Nous utilisons Backbone's sur() méthode afin d'écouter notre événement personnalisé. Nous spécifions le filterByType () méthode en tant que fonction de gestionnaire pour cet événement en utilisant le deuxième argument de sur(), et peut également définir le contexte de la fonction de rappel en définissant ce comme troisième argument. le ce objet se réfère ici à notre vue principale.

Dans notre filterByType fonction, nous réinitialisons la collection afin de la repeupler avec tous les modèles ou les modèles filtrés. Nous pouvons également nous lier au réinitialiser event afin de repeupler la collection avec des instances de modèle. Nous pouvons également spécifier une fonction de gestionnaire pour cet événement. Le plus grand avantage est que nous en avons déjà une. Ajoutez la ligne de code suivante directement après le changement liaison d'événement:

this.collection.on ("reset", this.render, this);

Dans ce cas, nous écoutons pour le réinitialiser événement et la fonction que nous souhaitons invoquer est la collection rendre() méthode. Nous spécifions également que le rappel doit utiliser ce (comme dans l'instance de la vue principale) en tant que contexte lors de son exécution. Si nous ne fournissons pas ce comme troisième argument, nous ne pourrons pas accéder à la collection à l’intérieur du rendre() méthode quand il gère la réinitialiser un événement.

À ce stade, nous devrions maintenant constater que nous pouvons utiliser la zone de sélection pour afficher des sous-ensembles de nos contacts. La raison pour laquelle nous avons défini le silencieux option à vrai dans notre filterByType () La méthode est telle que la vue ne soit pas rendue inutilement lorsque nous réinitialisons la collection au début de la deuxième branche du conditionnel. Nous devons faire cela pour pouvoir filtrer par un type, puis par un autre type sans perdre de modèle..


Routage

Donc, ce que nous avons eu jusqu'à présent est correct, nous pouvons filtrer nos modèles à l'aide de la zone de sélection. Mais ne serait-il pas génial de pouvoir également filtrer la collection à l'aide d'une URL? Le module de routeur de Backbone nous donne cette possibilité, voyons comment, et en raison de la manière bien découplée avec laquelle nous avons structuré notre filtrage jusqu'à présent, il est très facile d'ajouter cette fonctionnalité. Nous devons d’abord étendre le module routeur; ajoutez le code suivant après la vue principale:

var ContactsRouter = Backbone.Router.extend (routes: "filtre /: type": "urlFilter", urlFilter: fonction (type) directory.filterType = type; directory.trigger ("change: filterType"); );

La première propriété que nous définissons dans l'objet passé au routeur étendre() la méthode est itinéraires, ce qui devrait être un littéral d'objet où chaque clé est une URL à rechercher et chaque valeur à une fonction de rappel lorsque l'URL correspond. Dans ce cas, nous recherchons des URL commençant par #filtre et finir avec autre chose. La partie de l'URL après le filtre/ une partie est passée à la fonction que nous spécifions en tant que fonction de rappel.

Dans cette fonction, nous définissons ou mettons à jour le filterType propriété de la vue principale puis déclencher notre coutume changement événement une fois de plus. C’est tout ce dont nous avons besoin pour ajouter une fonctionnalité de filtrage à l’aide de l’URL. Nous devons toutefois créer une instance de notre routeur, ce que nous pouvons faire en ajoutant la ligne de code suivante directement après la fin de la procédure. DirectoryView instanciation:

var contactsRouter = new ContactsRouter ();

Nous devrions maintenant pouvoir entrer une URL telle que # filtre / famille et la vue se re-rendra pour montrer uniquement les contacts avec la famille de types:

Donc c'est plutôt cool non? Mais il manque encore une partie: comment les utilisateurs sauront-ils utiliser nos belles URL? Nous devons mettre à jour la fonction qui gère les événements de l'interface utilisateur sur le serveur. élément de sorte que l'URL soit mise à jour lorsque la zone de sélection est utilisée.

Pour ce faire, il faut deux étapes. Tout d'abord, nous devrions activer le support de l'historique de Backbone en démarrant le service d'historique après l'initialisation de notre application. ajoutez la ligne de code suivante à la fin de notre fichier de script (directement après l’initialisation de notre routeur):

Backbone.history.start ();

À partir de ce moment, Backbone surveillera l'URL des changements de hachage. Maintenant, lorsque nous voulons mettre à jour l'URL après que quelque chose se passe, nous appelons simplement le naviguer() méthode de notre routeur. Changer la filterByType () méthode pour qu'il apparaisse comme ceci:

filterByType: function () if (this.filterType === "all") this.collection.reset (contacts); contactsRouter.navigate ("filter / all");  else this.collection.reset (contacts, silent: true); var filterType = this.filterType, filtré = _.filter (this.collection.models, function (item) return item.get ("type") === filterType;); this.collection.reset (filtré); contactsRouter.navigate ("filter /" + filterType); 

Désormais, lorsque la boîte de sélection est utilisée pour filtrer la collection, l'URL est mise à jour et l'utilisateur peut alors mettre en signet ou partager l'URL, et les boutons Précédent et Suivant du navigateur permettent de naviguer entre les états. Depuis la version 0.5, Backbone prend également en charge l’API pushState. Toutefois, pour que cela fonctionne correctement, le serveur doit pouvoir afficher les pages demandées, que nous n’avons pas configurées pour cet exemple, et qui utilisent donc le module d’historique standard..


Résumé

Dans cette partie du didacticiel, nous avons examiné quelques autres modules de Backbone, en particulier les modules Router, History et Events. Nous avons maintenant examiné tous les différents modules fournis avec Backbone..

Nous avons également examiné d'autres méthodes de soulignement, notamment filtre(), que nous avons utilisé pour filtrer notre collection à seulement les modèles contenant un type spécifique.

Enfin, nous avons examiné le module Routeur de Backbone, qui nous permettait de définir des itinéraires pouvant être mis en correspondance par notre application afin de déclencher des méthodes, ainsi que le module Historique que nous pouvons utiliser pour mémoriser l’état et maintenir l’URL mise à jour avec des fragments de hachage..

Un point à retenir est la nature faiblement couplée de notre fonctionnalité de filtrage; Lorsque nous avons ajouté le filtrage via le menu de sélection, il était très facile et rapide de venir ensuite et d’ajouter une méthode de filtrage totalement nouvelle sans avoir à changer notre filtre() méthode. C'est l'une des clés pour réussir à créer des applications JavaScript non triviales, maintenables et évolutives. Si nous le voulions, il serait très facile d’ajouter une autre méthode de filtrage totalement nouvelle, qui doit changer de méthode de filtrage..

Dans la prochaine partie de cette série, nous allons revenir à l'utilisation de modèles et voir comment nous pouvons les supprimer et en ajouter de nouveaux à la collection..