I. Préparation

Avant de commencer ce tutoriel, vous aurez besoin de ce qui suit :

  • alexyoung/dailyjs-backbone-tutorial en version 465523f ;
  • la clé de l'API de la partie 2 ;
  • l'ID client de la partie 2 ;
  • la mise à jour de app/js/config.js avec vos clés.

Pour consulter la source, exécutez les commandes suivantes (ou utilisez un outil approprié pour Git GUI) :

 
Sélectionnez
git clone git@github.com:alexyoung/dailyjs-backbone-tutorial.git
cd dailyjs-backbone-tutorial
git reset --hard 465523f

II. Liste active

La fois dernière, j'ai démontré comment faire une implémentation personnalisée « create » de Backbone.sync, ainsi que des vues appropriées et des modèles pour l'ajout de nouvelles listes. Si vous vous souvenez, j'ai créé une vue pour modifier les listes également parce que c'était fort semblable à AddListView. Il était donc logique d'en hériter.

Avant qu'une liste puisse être modifiée, nous avons besoin d'un moyen de sélection des listes. Il est logique d'avoir toujours une liste active dans cette application, donc il devrait y avoir un moyen de sauver cet état quelque part. En outre, lorsque qu'on charge les listes depuis le serveur, une liste par défaut doit déjà être sélectionnée.

Pour être compatible avec les modèles utilisés pour les collections et les affichages, nous devrions ajouter un objet models pour le suivi des instances de modèles. L'un d'eux peut être l'objet activeList.

Ouvrez le fichier app/js/app.js et ajoutez une propriété models ainsi que le positionnement de activeModel après que les listes sont chargées :

 
Sélectionnez
App.prototype = {
  views: {},
  collections: {},
  models: {},
  connectGapi: function() {
    var self = this;
    this.apiManager = new ApiManager(this);
    this.apiManager.on('ready', function() {
      self.collections.lists.fetch({ data: { userId: '@me' }, success: function(res) {
        self.models.activeList = self.collections.lists.first();
        self.views.listMenu.render();
      }});
    });
  }
};

Maintenant, ouvrez le fichier app/js/views/lists/menu.js et vérifiez si l'activeModel est bien le modèle actuellement utilisé lors de l'affichage de liste :

 
Sélectionnez
renderMenuItem: function(model) {
  var item = new ListMenuItemView({ model: model });
  this.$el.append(item.render().el);

  if (model.get('id') === bTask.models.activeList.get('id')) {
    item.open();
  }
},

Si le modèle correspond, il va déclencher un événement open sur la vue. Maintenant, ouvrez le fichier app/js/views/lists/menuitem.js et faites que ListMenuItemView suive l'activeModel :

 
Sélectionnez
open: function() {
  bTask.models.activeList = this.model;
  return false;
}

L'application est maintenant en mesure de détecter la liste sélectionnée. Cela facilitera l'ajout de tâches, car afin d'ajouter des tâches, nous avons besoin de savoir à quelle tasklist les ajouter.

III. Formulaire de modification de liste

Ouvrez le fichier app/js/views/app.js. L'objectif de cet exercice est d'afficher le formulaire de modification, rempli avec les valeurs correctes, lorsque l'utilisateur clique sur le lien « Modifier la liste ». Il va être similaire à la méthode addList, alors vous pouvez essayer de faire cette partie vous-même si vous le souhaitez.

D'abord, faites-le charger la classe EditListView :

 
Sélectionnez
define([
  'text!templates/app.html'
, 'views/lists/add'
, 'views/lists/edit'
],

function(template, AddListView, EditListView) {

Ensuite, ajoutez #edit-list-button à l'événement :

 
Sélectionnez
events: {
  'click #add-list-button': 'addList'
, 'click #edit-list-button': 'editList'
},

Pour finir, ajoutez la méthode editList pour instancier un formulaire EditListView basé sur l'activeList :

 
Sélectionnez
editList: function() {
  var form = new EditListView({ model: bTask.models.activeList });

  this.$el.find('#list-editor').html(form.render().el);
  form.$el.find('input:first').focus();

  return false;
}

Ceci est très similaire à la méthode addList. Ils peuvent facilement utiliser la même méthode, juste avec les différents modèles :

 
Sélectionnez
listForm: function(form) {
  this.$el.find('#list-editor').html(form.render().el);
  form.$el.find('input:first').focus();

  return false;
},

addList: function() {
  return this.listForm(new AddListView({ model: new bTask.collections.lists.model({ title: '' }) }));
},

editList: function() {
  return this.listForm(new EditListView({ model: bTask.models.activeList }));
}

C'est fait !

IV. Sauvegarder les modifications

La méthode Backbone.sync doit être mise à jour pour faire face aux modifications des éléments. Ceci est très similaire à la création d'éléments (dans le fichier app/js/gapi.js) :

 
Sélectionnez
// Aux alentours de la ligne 97, après 'create'
case 'update':
  requestContent['resource'] = model.toJSON();
  request = gapi.client.tasks[model.url].update(requestContent);
  Backbone.gapiRequest(request, method, model, options);
break;

Une légère complication est due au fait que l'API de Google requiert une propriété de la liste des tâches dans l'objet passé pour être mis à jour. Ce n'est pas très clairement documenté (vous remarquerez que la référence tasklists/updateTasklists: update n'a pas un exemple de JavaScript).

Plutôt que de faire en sorte que les modèles Backbone soient au courant de cela, il est préférable de mettre la logique dans Backbone.sync. Ainsi, tous les trucs liés à Google sont au même endroit.

Ajoutez une autre instruction switch pour insérer les paramètres ID requis selon le type de modèle exploité :

 
Sélectionnez
Backbone.sync = function(method, model, options) {
  var requestContent = {};
  options || (options = {});

  switch (model.url) {
    case 'tasks':
      requestContent.task = model.get('id');
    break;

    case 'tasklists':
      requestContent.tasklist = model.get('id');
    break;
  }

Les listes devraient maintenant être modifiables. Mais il y a une chose qui reste à faire : montrer que la liste sélectionnée est « active ».

V. Listes de sélection

Ouvrez le fichier app/js/views/lists/menuitem.js et changez open pour suivre l'affichage du menu actif et ajoutez un nom de classe à l'élément de la vue :

 
Sélectionnez
open: function() {
  if (bTask.views.activeListMenuItem) {
    bTask.views.activeListMenuItem.$el.removeClass('active');
  }

  bTask.models.activeList = this.model;
  bTask.views.activeListMenuItem = this;
  this.$el.addClass('active');

  return false;
}

Chaque fois qu'une vue est ouverte, bTask.views.activeListMenuItem servira à stocker une référence vers celui-ci. Remarquez comment j'ai utilisé this.$el. Une bonne partie des développeurs expérimentés de Backbone vous diront que pour faire cela, il vaut mieux utiliser $() de jQuery pour rechercher des éléments basés sur un sélecteur. L'idée est d'utiliser jQuery un minimum et d'être le plus déclaratif possible avec Backbone.

Le fait d'avoir une référence à bTask.views.activeListMenuItem est-il mieux que d'utiliser $('.list-menu-item').removeClass('active') ? Il est difficile de le dire, j'ai souvent remarqué des gens plonger dans jQuery, parfois avec logique.

Cela soulève la question suivante : devons-nous vraiment suivre la liste active à l'aide d'une référence à un modèle ? La ListMenuItemView contient déjà une référence au modèle, et la plupart du code de Backbone est vraiment soucieux de la modélisation de l'interface utilisateur, et non d'un état interne supplémentaire. Nous allons essayer de supprimer la référence à bTask.models.

Ouvrez le fichier app/js/app.js et supprimez l'objet models, puis supprimez la ligne qui définit activeList. Ensuite, allez dans le fichier app/js/views/lists/menuitem.js et changez la méthode open pour ne faire référence qu'aux vues :

 
Sélectionnez
open: function() {
  if (bTask.views.activeListMenuItem) {
    bTask.views.activeListMenuItem.$el.removeClass('active');
  }

  bTask.views.activeListMenuItem = this;
  this.$el.addClass('active');

  return false;
}

Ensuite, ouvrez la classe de AppView dans le fichier app/js/views/app.js et veillez à ce que editList utilise bTask.views.activeListMenuItem.model. Enfin, faites que le fichier app/js/views/lists/menu.js active l'élément par défaut (la première liste) :

 
Sélectionnez
renderMenuItem: function(model) {
  var item = new ListMenuItemView({ model: model });
  this.$el.append(item.render().el);

  if (!bTask.views.activeListMenuItem) {
    bTask.views.activeListMenuItem = item;
  }
  
  if (model.get('id') === bTask.views.activeListMenuItem.model.get('id')) {
    item.open();
  }
},

J'ai l'impression qu'éviter de suivre un état de l'application interne est une erreur dans Backbone. Au lieu de cela, les vues doivent redoubler d'efforts. Est-ce une bonne idée ? Cela dépend sans doute de la nature de l'application.

Pour rendre l'interface plus claire, vous pouvez ajouter li.active {font-weight : bold} au fichier app/css/app.css.

VI. Résumé

Dans cette partie, nous sommes partis du code du tutoriel 6 pour permettre de modifier des listes. Même si c'est assez simple, l'application devait changer pour reconnaître la liste active.

La règle générale dans Backbone est d'utiliser des objets jQuery mis en cache (ou Zepto), c'est pourquoi vous verrez beaucoup d'appels du genre this.$el au lieu de $(). Je suggère une autre règle qui s'ajoute à cela : faire des vues qui font le travail et éviter de s'appuyer sur l'état externe aux vues.

Le code source de ce tutoriel se trouve ici : alexyoung/dailyjs-backbone-tutorial en version 0953c5d.

VII. Remerciements

Cet article a été publié avec l'aimable autorisation de Alex Young. L'article original peut être lu sur le site DailyJSDailyJS : Backbone.js Tutorial: Editing ListsBackbone.js Tutorial: Editing Lists.
Je remercie également _Max_ pour sa relecture attentive et assidue.