web-dev-qa-db-fra.com

Backbone.js - Méthode correcte de filtrage et d'affichage des données de collection dans une vue

J'ai une énorme liste de tâches chargées au début.
Je veux les afficher en fonction de la liste/boîte de réception sélectionnée, afin qu'il n'y ait pas de charges supplémentaires pour chaque liste.

window.Task = Backbone.Model.extend({});

window.TasksCollection = Backbone.Collection.extend({
    model: Task,
    url: '/api/tasks',
    inbox: function() {
        return this.filter(function(task) {
            return task.get('list') == null;
        });
    },
    list: function(id) {
        return this.filter(function(task) {
            return task.get('list') == id;
        });
    }
});

window.tasks = new TasksCollection;

window.TaskView = Backbone.View.extend({
    tagName: 'li',
    template: _.template($('#item-template').html()),
    initialize: function() {
        _.bindAll(this, 'render', 'close');
        this.model.bind('change', this.render);
        this.model.view = this;
    },
    render: function() {
        $(this.el).html(this.template(this.model.toJSON()));
        this.setContent();
        return this;
    },
});

window.TasksView = Backbone.View.extend({
    el: '#todo-list',
    collection: tasks,
    initialize: function() {
        _.bindAll(this, 'render');
        this.collection.bind('reset', this.render);
        this.collection.fetch();
    },
    render: function() {
        var t = this;
        $(t.el).html('');
        this.collection.each(function(task) {
            var view = new TaskView({ model:task });
            $(t.el).append( view.render().el );
        });
        return this;
    },
});

window.Nicetask = Backbone.Router.extend({
    routes: {
        '':             'inbox',
        '/inbox':       'inbox',
        '/list/:id':    'list',
    },
    initialize: function() {
        _.bindAll(this, 'inbox', 'list');
        window.tasksView = new TasksView;
    },
    inbox: function() {
        tasks.reset( tasks.inbox() );
    },
    list: function(id) {
        tasks.reset( tasks.list(id) );
    }
});

Ce code fonctionne, mais la fonction reset() supprime les autres tâches de la liste réelle de la collection de tâches. Et sur un autre itinéraire, la collection de tâches est vide.

Existe-t-il un moyen raisonnable d'y parvenir? merci pour toute idée.

ps: backbone novice


MISE À JOUR

Merci à @sled et @ibjhb pour les commentaires, voici un extrait de la solution de travail.

window.TasksView = Backbone.View.extend({
    el: '#todo-list',
    collection: Backbone.Collection.extend(),
    initialize: function() {
        _.bindAll(this, 'render', 'addOne', 'addAll');
        this.collection.bind('add', this.addOne);
        this.collection.bind('reset', this.render);
    },
    render: function(data) {
        $(this.el).html('');
        _.each(data, function(task) {
            this.addOne(task);
        }, this);
        return this;
    },
    addOne: function(task) {
        var view = new TaskView({ model:task });
        $(this.el).append( view.render().el );
    },
});

window.Nicetask = Backbone.Router.extend({
    routes: {
        '':             'inbox',
        '/inbox':       'inbox',
        '/today':       'today',
        '/list/:id':    'list',
    },
    initialize: function() {
        _.bindAll(this, 'inbox', 'today');
        window.tasksView = new TasksView;
        window.menuView = new MenuListView;
        tasks.fetch();
    },
    inbox: function() {
        tasksView.render( tasks.inbox() );
    },
    today: function() {
        tasksView.render( tasks.today() );
    },
    list: function(id) {
        tasksView.render( tasks.list(id) );
    }
});
39
Juraj Ivan

Je pense que vous devez utiliser une autre collection. Par exemple, dans votre boîte de réception, procédez comme suit:

inbox: function(){
    currentCollection = new TasksCollection(tasks.inbox());
}

Je n'ai pas testé cela mais quand vous faites un .reset (); vous supprimez tous vos modèles et chargez ceux passés.

4
James Brown

@sled il y a des fautes de frappe dans le code que vous avez publié, voir les commentaires en ligne. Avez-vous posté cela en tant que projet quelque part?

// add models
add: function(models, options) {
  // TYPO: next line was missing, so single models not handled.
  models = _.isArray(models) ? models.slice() : [models];

  var self = this;

  models = _.filter(models, this.filter);

  // return if no models exist
  // TYPO: returned undefined, so was not chainable
  if(models.length == 0) { return this; }

  // actually add the models to the superset
  this.superset.add(models, options);
  return this;
},

// remove models
remove: function(models, options) {
  // TYPO: next line was missing, so single models not handled.
  models = _.isArray(models) ? models.slice() : [models];

  // remove model from superset
  this.superset.remove(_.filter(_.filter(models, function(cm) {
    // TYPO: not 'm != null', causes error to be thrown
    return cm != null;
  }), this.filter), options);
  // TYPO: missing return so not chainable
  return this;
},
4
bazzargh

un amendement rapide à votre solution, vous utilisez

$(this.el).html('');

Ma compréhension est que les vues et les liaisons d'événements associées existeront toujours dans la mémoire du navigateur, vous devez donc idéalement utiliser view.remove () sur la TaskView pour effacer correctement les liaisons d'événements ainsi que le code HTML.


Il s'agit d'une approche légèrement différente de la réponse, car je cherchais une solution à un problème similaire, j'espère que cela pourra aider les autres.

Mon problème: - filtrer une collection complète par attributs du modèle. par exemple. un utilisateur clique sur la vue des modèles, obtient une liste (de certains) des attributs, la sélection d'un attribut filtre la collection pour n'afficher que ceux ayant la même valeur.

La route que je prends consiste à appeler une méthode sur la collection à partir de la vue, dans mon cas, la vue est spécifique à un modèle, donc:

this.model.collection.myFilter(attr,val);

où attr est un attribut du modèle associé à la collection, puis dans le filtre quelque chose comme

myFilter: function(attr, val){
    var groupByAttr = this.groupBy(function(article){
        var res = (val === undefined)? true : (article.get(attr) == val);
        article.set({selected:res});
        return res;
    });
    return groupByAttr;
}

J'ai utilisé ._ groupBy car cela renvoie 2 tableaux (positifs/négatifs) qui peuvent être utiles. En définissant l'attribut mode "sélectionné" et en le liant dans la vue du modèle, je peux facilement basculer une classe qui affiche ou masque la vue.

if (val === undefined) est ajouté comme un moyen simple d'effacer un filtre en appelant la même méthode sans valeur.

1
andy t