web-dev-qa-db-ja.com

Backbone.js-ビューでコレクションデータをフィルタリングおよび表示する正しい方法

開始時に読み込まれるタスクの膨大なリストを持っています。
選択したリスト/受信トレイに応じて表示したいので、各リストに追加の負荷がかからないようにします。

_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) );
    }
});
_

このコードは機能しますが、reset()関数は実際のリストの他のタスクをタスクコレクションから削除します。また、別のルートでは、タスクコレクションは空です。

これを達成するための合理的な方法はありますか?どんなアイデアもありがとう。

pS:バックボーン初心者


[〜#〜] update [〜#〜]

コメントについては、@ sledと@ibjhbにThxを送信してください。ここに、実際のソリューションのスニペットを示します。

_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

別のコレクションを使用する必要があると思います。たとえば、受信トレイで次の操作を行います。

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

私はこれをテストしていませんが、.reset()を実行するとき;すべてのモデルを削除し、渡されたモデルをロードしています。

4
James Brown

@sledは、投稿したコードにタイプミスがあります。インラインでコメントを参照してください。これをプロジェクトとしてどこかに投稿しましたか?

// 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

あなたのソリューションへの1つの簡単な修正、あなたは使用しています

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

私の理解では、あなたのビューと関連するイベントバインディングはブラウザのメモリに残っているので、理想的にはTaskViewでview.remove()を使用してイベントバインディングとhtmlを正しくクリアする必要があります。


私は同様の問題の解決策を探していたので、これは答えに対するわずかに異なる見解です。これが他の人の助けになることを願っています。

私の問題:-モデルの属性によって完全なコレクションをフィルタリングする。例えば。ユーザーはモデルビューをクリックし、属性の一部(一部)を取得します。属性を選択すると、コレクションがフィルター処理され、同じ値を持つもののみが表示されます。

私が取っているルートは、ビューからコレクションのメソッドを呼び出すことです。私の場合、ビューはモデルに固有です。

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

attrはコレクションに関連付けられたモデルの属性であり、フィルターでは次のようになります

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;
}

._ groupByを使用しました。これは、使用可能な2つの配列(正/負)を返すためです。 mode属性を "selected"に設定し、モデルビューでこれにバインドすることにより、ビューを表示または非表示にするクラスを簡単に切り替えることができます。

if(val === undefined)は、値なしで同じメソッドを呼び出してフィルターをクリアする簡単な方法として追加されます。

1
andy t