I. Préparation

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

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

II. Tâches, la suite

Image non disponible

L'application consiste maintenant en l'affichage des listes de tâches. Mais vous ne pouvez pas encore interagir avec elles. Ce tutoriel couvre :

  • l'ajout de tâches ;
  • la modification de tâches ;
  • la suppression de tâches ;
  • l'activation/désactivation de tâches.

La plupart de ces contenus s'appuie sur ce que nous avons fait avec les listes, mais c'est une bonne pratique si vous cherchez plus d'expérience avec Backbone.

III. L'ajout de tâches

Ouvrez le fichier app/js/views/tasks/index.js et ajoutez la gestion d'un événement pour addTask :

 
Sélectionnez
events: {
  'submit .add-task': 'addTask'
},

Dans la méthode initialize, ajoutez un écouteur à la collection de cette classe :

 
Sélectionnez
initialize: function() {
  this.children = [];
  this.collection.on('add', this.renderTask, this);
},

Cela affiche automatiquement les nouvelles tâches lorsqu'elles sont ajoutées à la collection.

La méthode addTask doit appeler Task.prototype.save pour faire persister la tâche à l'aide des API Google après son instanciation avec une référence à la liste actuelle. Cela devrait également afficher la tâche une fois qu'elle a été enregistrée. J'ai mis { at: 0} car Google Tasks place les nouvelles tâches en haut d'une liste. Notez que je préfère ajouter uniquement des tâches une fois qu'elles ont été enregistrées avec succès, ce qui fait que cette application nécessite toujours une connexion Internet. Il peut être préférable de faire un enregistrement dans une base de données locale et de synchroniser avec Google par la suite, mais nous n'allons pas faire cela ici.

 
Sélectionnez
collection.fetch({ data: { tasklist: this.model.get('id') }, // ...addTask: function() {
  var $input = this.$el.find('input[name="title"]')
    , task = new this.collection.model({ tasklist: this.model.get('id') })
    , self = this
    ;

  task.save({ title: $input.val() }, {
    success: function() {
      self.collection.add(task, { at: 0 });
    }
  });
  $input.val('');

  return false;
},

renderTask: function(task, list, options) {
  var item = new TaskView({ model: task, parentView: this })
    , $el = this.$el.find('#task-list');
  if (options && options.at === 0) {
    $el.prepend(item.render().el);
  } else {
    $el.append(item.render().el);
  }
  this.children.push(item);
},

La méthode renderTask reçoit l'argument options et l'utilise pour déterminer la façon d'ajouter la tâche à la liste. La raison pour laquelle je n'ajoute pas simplement de nouvelles tâches est que la méthode render peut maintenant être refactorisée pour utiliser cette méthode :

 
Sélectionnez
render: function() {
  this.$el.html(this.template());

  var $el = this.$el.find('#task-list')
    , self = this;

  this.collection.fetch({ data: { tasklist: this.model.get('id') }, success: function() {
    self.collection.each(function(task) {
      task.set('tasklist', self.model.get('id'));
      self.renderTask(task);
    });
  }});
}

Ouvrez le fichier app/js/views/lists/menuitem.js et faites passer la tâche dans une collection de Tasks au niveau de la méthode open où elle instancie bTask.views.tasksIndexView :

 
Sélectionnez
bTask.views.tasksIndexView = new TasksIndexView({ collection: new Tasks({ tasklist: this.model.get('id') }), model: this.model });

Vous aurez besoin de modifier l'instruction define en haut du fichier pour inclure la collection Tasks :

 
Sélectionnez
define(['text!templates/lists/menuitem.html', 'views/tasks/index', 'collections/tasks'], function(template, TasksIndexView, Tasks) {

En raison de la manière dont fonctionnent les API Google, vous aurez besoin de faire un petit changement au fichier app/js/gapi.js pour insérer un ID tasklist dans le requestContent :

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

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

L'ajout de tâches devrait maintenant fonctionner. Il n'y a pas besoin d'un nouveau template parce qu'il a déjà été ajouté dans le cadre d'un tutoriel précédent.

IV. La modification de tâches

Pour modifier les tâches, certaines choses sont nécessaires :

  • un template de formulaire approprié ;
  • une Backbone.View ;
  • une gestion des événements pour enregistrer la tâche.

Voici le template qui doit être enregistré dans le fichier app/js/templates/tasks/edit.html :

 
Sélectionnez
<fieldset>
  <legend>
    Propriétés de la tâche
    <a href="#" data-task-id="" class="pull-right delete-task btn"><i class="icon-trash"></i></a>
  </legend>
  <div class="control-group">
    <label for="task_title">Titre</label>
    <input type="text" class="input-block-level" name="title" id="task_title" value="" placeholder="Le titre de la tâche">
  </div>
  <div class="control-group">
    <label class="radio"><input type="radio" name="status" value="needsAction" >Besoin d'actions</label>
    <label class="radio"><input type="radio" name="status" value="completed" >Complété</label>
  </div>
  </div>
  <div class="control-group">
    <label for="task_notes">Informations</label>
    <textarea class="input-block-level" name="notes" id="task_notes" placeholder="Informations à propos de cette tâche"></textarea>
  </div>
</fieldset>
<div class="form-actions">
  <button type="submit" class="btn btn-primary">Enregistrer les modifications</button>
  <button class="cancel btn">Fermer</button>
</div>

J'ai inclus toutes les classes Bootstrap habituelles et le balisage dans ce fragment de formulaire donc cela sera agréable à l'affichage.

La vue correspondante (app/js/views/tasks/edit.js) devrait ressembler à ceci :

 
Sélectionnez
define(['text!templates/tasks/edit.html'], function(template) {
  var TaskEditView = Backbone.View.extend({
    tagName: 'form',
    className: 'well edit-task',
    template: _.template(template),

    events: {
      'submit': 'submit'
    , 'click .cancel': 'cancel'
    },

    initialize: function() {
      this.model.on('change', this.render, this);
    },

    render: function() {
      this.$el.html(this.template(this.model.toJSON()));
      return this;
    },

    submit: function() {
      var title = this.$el.find('input[name="title"]').val()
        , notes = this.$el.find('textarea[name="notes"]').val()
        , status = this.$el.find('input[name="status"]:checked').val()
        ;

      this.model.set('title', title);
      this.model.set('notes', notes);

      if (status !== this.model.get('status')) {
        this.model.set('status', status);
        if (status === 'needsAction') {
          this.model.set('completed', null);
        }
      }

      this.model.save();
      return false;
    },

    cancel: function() {
      this.remove();
      return false;
    }
  });

  return TaskEditView;
});

Cela met en place un événement submit pour la capture de l'envoi du formulaire et également un événement pour la fermeture du formulaire, lequel est lié à la méthode cancel.

Maintenant, ajoutez une méthode au fichier app/js/views/tasks/index.js qui invoque TaskEditView :

 
Sélectionnez
editTask: function(task) {
  if (this.taskEditView) {
    this.taskEditView.remove();
  }
  this.taskEditView = new TaskEditView({ model: task });
  this.$el.find('#selected-task').append(this.taskEditView.render().el);
}

Et assurez-vous qu'elle charge TaskEditView :

 
Sélectionnez
define(['text!templates/tasks/index.html', 'views/tasks/task', 'views/tasks/edit', 'collections/tasks'], function(template, TaskView, TaskEditView, Tasks) {

Cela doit être appelé par une tâche individuelle, donc ouvrez le fichier app/js/views/tasks/task.js et ajoutez ceci à la méthode open :

 
Sélectionnez
this.parentView.editTask(this.model);

Ces deux vues ont beaucoup de couplage entre elles, ce qui rend difficile la réutilisation de TaskView. Cependant, y a-t-il un sens à les utiliser sans TasksIndexView ? C'est le genre de question que vous allez vous poser en essayant d'écrire du code Backbone maintenable.

V. La suppression de tâches

Ajoutez une méthode destroy au fichier app/js/views/tasks/edit.js :

 
Sélectionnez
destroy: function() {
  this.model.destroy();
  return false;
}

Ensuite, liez la méthode à l'événement sur l'icône de la corbeille (.delete-task), puis liez un événement sur le modèle en cours de suppression :

 
Sélectionnez
events: {
  'submit': 'submit'
, 'click .cancel': 'cancel'
, 'click .delete-task': 'destroy'
},

initialize: function() {
  this.model.on('change', this.render, this);
  this.model.on('destroy', this.remove, this);
},

VI. L'activation/désactivation de tâches

Voici la cerise sur le gâteau, activation/désactivation de la tâche ! Avec ce changement, l'application commencera vraiment à être une réelle application de liste de tâches. Ouvrez le fichier app/js/views/tasks/task.js et ajoutez une liaison d'événements pour les cases à cocher dans la liste. Un événement change est nécessaire pour cela :

 
Sélectionnez
events: {
  'click': 'open'
, 'change .check-task': 'toggle'
},

Puis la méthode toggle doit simplement activer/désactiver l'attribut status selon l'état de la case à cocher :

 
Sélectionnez
toggle: function() {
  var id = this.model.get('id')
    , $el = this.$el.find('.check-task')
    ;

  this.model.set('status', $el.attr('checked') ? 'completed' : 'needsAction');
  if (this.model.get('status') === 'needsAction') {
    this.model.set('completed', null);
  }

  this.model.save();
  return false;
}

La nomenclature de Google pour l'état de la tâche propose completed et needsAction. Il faut un peu creuser dans la documentation pour le savoir.

VII. Résumé

Il a fallu un certain temps pour arriver aussi loin mais travailler avec des API inconnues avec leurs idiosyncrasies demande beaucoup de patience. Et si vous essayez d'exécuter le code de ce projet, assurez-vous que vous avez réellement des tâches et des listes dans Gmail. Cela ne fonctionne pas s'il y en n'a aucune. Je corrigerai ça plus tard !

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

VIII. 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: Oh No Not More TasksBackbone.js Tutorial: Oh No Not More Tasks.
Je remercie également _Max_ pour sa relecture attentive et assidue.