I. Préparation

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

  • alexyoung/dailyjs-backbone-tutorial en version 705bcb4 ;
  • 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 705bcb4

II. Utiliser Backbone et les plugins jQuery

Bien que Backbone ne doit pas être utilisé spécifiquement avec jQuery, beaucoup de gens l'utilisent avec jQuery (et RequireJS) pour avoir accès à divers plugins déployés par la communauté jQuery. Dans ce tutoriel, je vais vous expliquer comment utiliser des plugins jQuery avec les projets Backbone et la façon de trouver ceux qui fonctionneront bien.

L'exemple que j'ai utilisé intègre un plugin « sortable » (triable) de type « drag-and-drop » (glisser-déposer) pour permettre de réorganiser les tâches.

III. HTML5 Sortable

Le plugin que j'ai utilisé pour faire du glisser-déposer est le plugin HTML5 SortableHTML5 Sortable créé par Ali Farhadi. La raison pour laquelle j'ai utilisé ce plugin particulier est qu'il a une API simple basée sur des événements, ce qui permet au plugin d'être déchargé et de trier les événements pour les capturer et les mettre en attente de réponse. Il lui suffit d'un élément conteneur et des éléments enfants qui ont besoin d'être triés. La liste non ordonnée des tâches relatives à ce projet se traduit directement par le balisage attendu.

Il est parfois plus facile d'écrire uniquement les données d'attributs aux éléments, plutôt que d'essayer de créer des relations entre les nœuds DOM utilisés par les plugins et les modèles. HTML5 Sortable émet un événement 'sortupdate' quand un nœud a été déplacé par glisser-déposer, et il va passer l'élément pertinent à l'écouteur de callback. De là, nous avons besoin de savoir quel modèle a changé, puis de le traduire en quelque chose que l'API de Google peut comprendre.

IV. Charger le plugin avec RequireJS

Dans un précédent tutoriel, j'ai démontré comment charger des bibliothèques non AMD à l'aide de RequireJS. Si vous voulez un petit récapitulatif, vérifiez le fichier app/js/main.js et regardez la propriété shim dans la configuration de RequireJS :

 
Sélectionnez
requirejs.config({
  baseUrl: 'js',

  paths: {
    text: 'lib/text'
  },

  shim: {
    'lib/underscore-min': {
      exports: '_'
    },
    'lib/backbone': {
      deps: ['lib/underscore-min']
    , exports: 'Backbone'
    },
    'app': {
      deps: ['lib/underscore-min', 'lib/backbone', 'lib/jquery.sortable']
    }
  }
});

La propriété 'app' exprime une dépendance entre le fichier principal de l'application Backbone et lib/jquery.sortable. Ce qui signifie que /lib/jquery.sortable.js sera automatiquement chargé (ou compilé par r.js lors de la création d'une version de production de l'application).

V. API Google Tasks

Ce serait trop facile si l'API HTML5 Sortable correspondait tout à fait avec l'API Google. L'API Google a une méthode spécifique pour déplacer des tâches, qui est basée sur le principe que le déplacement d'une tâche occupe la place d'une autre :

 
Sélectionnez
gapi.client.tasks.tasks.move({ tasklist: listId, task: id, previous: previousId });

Le déplacement d'une tâche vers le haut de la liste est géré par le passage de null en guise de previous.

Ensuite, je vais vous expliquer comment créer certains éléments simples de l'interface pour faire du glisser-déposer, et puis, nous regarderons comment déplacer les tâches en traduisant l'API Google dans le code des modèles et collections Backbone.

VI. Implémentation : vues et templates

J'ai ajouté une petite icône de poignée à l'aide d'une icône Bootstrap et un élément d'ancrage dans le fichier app/js/templates/tasks/task.html :

 
Sélectionnez
<a href="#" class="handle pull-right"><i class="icon-move"></i></a>

Ensuite, j'ai ajouté le code mappé entre la vue Backbone et le plugin jQuery HTML5 Sortable dans le fichier app/js/views/tasks/index.js :

 
Sélectionnez
makeSortable: function() {
  var $el = this.$el.find('#task-list');
  if (this.collection.length) {
    $el.sortable('destroy');
    $el.sortable({ handle: '.handle' }).bind('sortupdate', _.bind(this.saveTaskOrder, this));
  }
},

saveTaskOrder: function(e, o) {
  var id = $(o.item).find('.check-task').data('taskId')
    , previous = $(o.item).prev()
    , previousId = previous.length ? $(previous).find('.check-task').data('taskId') : null
    , request
    ;

  this.collection.move(id, previousId, this.model);
},

La méthode makeSortable construit un élément qui apparaît « déplaçable » dans TasksIndexView. C'est-à-dire que le plugin HTML5 Sortable l'enrobe. La méthode sortupdate du plugin est alors liée à saveTaskOrder.

La méthode saveTaskOrder obtient l'ID de la tâche en cours en regardant la case à cocher, car j'avais déjà ajouté un attribut de données à cet élément dans le modèle. Cet ID est ensuite passé à la collection avec l'ID de la tâche précédente. Dans ce cas, la tâche précédente lui est adjacente afin que l'API Google comprenne comment faire pour déplacer la tâche.

La propriété de collection dans cette vue est une propriété de Tasks, nous allons donc jeter un œil à l'implémentation de la méthode move qui provoque les changements à faire persister.

VII. Implémentation : modèles et collections

Ouvrez le fichier app/js/collections/tasks.js et ajoutez-y une méthode move :

 
Sélectionnez
move: function(id, previousId, list) {
  var model = this.get(id)
    , toModel = this.get(previousId)
    , index = this.indexOf(toModel) + 1
    ;

  this.remove(model, { silent: true });
  this.add(model, { at: index, silent: true });

  // Faire persister les changements
  list.moveTask({ task: id, previous: previousId });
}

Cette méthode existe juste pour déclencher les appels remove et add sur la collection, ce qui implique donc de remanier les objets en interne. Elle appelle ensuite moveTask sur le modèle TaskList (dans le fichier app/js/models/tasklist.js) :

 
Sélectionnez
moveTask: function(options) {
  options['tasklist'] = this.get('id');
  var request = gapi.client.tasks.tasks.move(options);

  Backbone.gapiRequest(request, 'update', this, options);
}

La méthode gapiRequest constitue la base pour la méthode Backbone.sync personnalisée utilisée dans ce projet et dont j'ai parlé dans les tutoriels précédents. Je n'ai pas trouvé le moyen de faire exécuter un déplacement d'éléments à Backbone.sync d'une manière cohérente avec le fonctionnement de gapi.client.tasks.tasks.move, mais j'ai au moins pu réutiliser certaines des fonctionnalités de synchronisation en créant une requête et en appelant le gestionnaire de requêtes « standard ».

VIII. Résumé

Lorsque vous ne pouvez pas trouver un plugin Backbone adapté pour quelque chose et que vous voulez utiliser un plugin jQuery, mon conseil est de chercher des plugins qui ont des API basées sur les événements et qui peuvent être proprement déchargés. Ils seront plus faciles à intégrer dans vos vues Backbone.

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

IX. 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: jQuery Plugins and Moving TasksBackbone.js Tutorial: jQuery Plugins and Moving Tasks.

Je remercie également mumen pour sa relecture attentive et assidue.