Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/javascript/448.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Javascript Backbone.js-实施“最佳实践”;即时;搜寻_Javascript_Backbone.js - Fatal编程技术网

Javascript Backbone.js-实施“最佳实践”;即时;搜寻

Javascript Backbone.js-实施“最佳实践”;即时;搜寻,javascript,backbone.js,Javascript,Backbone.js,在我的主干应用程序中,有几个地方我希望对集合进行即时搜索,但我很难想出实现它的最佳方法 下面是一个快速实现。请记住,我的收藏可能包含200多种型号 var CollectionView = Backbone.View.extend({ template: $('#template').html(), initialize: function() { this.collection = new Backbone.Collection([ { first: 'Joh

在我的主干应用程序中,有几个地方我希望对集合进行即时搜索,但我很难想出实现它的最佳方法

下面是一个快速实现。请记住,我的收藏可能包含200多种型号

var CollectionView = Backbone.View.extend({

  template: $('#template').html(),

  initialize: function() {

    this.collection = new Backbone.Collection([
      { first: 'John', last: 'Doe' },
      { first: 'Mary', last: 'Jane' },
      { first: 'Billy', last: 'Bob' },
      { first: 'Dexter', last: 'Morgan' },
      { first: 'Walter', last: 'White' },
      { first: 'Billy', last: 'Bobby' }
    ]);
    this.collection.on('add', this.addOne, this);

    this.render();
  },

  events: {
    'keyup .search': 'search',
  },

  // Returns array subset of models that match search.
  search: function(e) {

    var search = this.$('.search').val().toLowerCase();

    this.$('tbody').empty(); // is this creating ghost views?

    _.each(this.collection.filter(function(model) {
      return _.some(
        model.values(), 
        function(value) {
          return ~value.toLowerCase().indexOf(search);
        });
    }), $.proxy(this.addOne, this));
  },

  addOne: function(model) {

    var view = new RowView({ model: model });
    this.$('tbody').append(view.render().el);
  },

  render: function() {

    $('#insert').replaceWith(this.$el.html(this.template));
      this.collection.each(this.addOne, this);
  }
});
每个模型都有一个小视图

var RowView = Backbone.View.extend({

  tagName: 'tr',

  events: {
    'click': 'click'
  },

  click: function () {
    // Set element to active 
    this.$el.addClass('selected').siblings().removeClass('selected');

    // Some detail view will listen for this.
    App.trigger('model:view', this.model);
  },

  render: function() {

    this.$el.html('<td>' + this.model.get('first') + '</td><td>' + this.model.get('last') + '</td>');
      return this;
  }
});

new CollectionView;
var RowView=Backbone.View.extend({
标记名:“tr”,
活动:{
“单击”:“单击”
},
单击:函数(){
//将元素设置为活动
此.el.addClass('selected').sides().removeClass('selected');
//一些细节视图将对此进行监听。
App.trigger('model:view',this.model);
},
render:function(){
this.$el.html(“”+this.model.get('first')+“”+this.model.get('last')+“”);
归还这个;
}
});
新集合视图;
问题1

在每次按下键时,我过滤集合,清空
tbody
,并呈现结果,从而为每个模型创建一个新视图。我刚刚创建了幻影视图,是吗?是否最好适当地破坏每个视图?或者我应该尝试管理我的
RowView
s。。。只创建一次每一个,并在其中循环以仅渲染结果?我的
CollectionView
中可能有一个数组?清空
t正文
后,
行视图
是否仍保留其
el
,还是现在为空,需要重新渲染

问题2,型号选择

您会注意到我在我的
行视图中触发了一个自定义事件。我想在某个地方有一个细节视图来处理该事件并显示整个模型。搜索列表时,如果我选择的模型仍保留在搜索结果中,我希望保留该状态并使其保留在详细视图中。一旦它不再出现在我的结果中,我将清空细节视图。所以我肯定需要管理一组视图,对吗?我考虑了一个双链接结构,其中每个视图指向它的模型,每个模型指向它的视图。。。但是,如果我将来要在我的模型上实现一个单例工厂,我不能把它强加在模型上/


那么,管理这些视图的最佳方法是什么?

与您的CollectionView关联的集合必须与您正在渲染的内容一致,否则您将遇到问题。您不必手动清空车身。您应该更新集合,并在CollectionView中侦听集合发出的事件,并使用该事件更新视图。在搜索方法中,您应该只更新集合,而不更新CollectionView。这是在CollectionView初始化方法中实现它的一种方法:


initialize: function() {
  //...

  this.listenTo(this.collection, "reset", this.render);
  this.listenTo(this.collection, "add", this.addOne);
}
在搜索方法中,只需重置集合,视图就会自动渲染:


search: function() {
  this.collection.reset(filteredModels);
}
其中,
filteredModels
是匹配搜索查询的模型数组。请注意,一旦使用筛选的模型重置集合,您将无法访问搜索之前原始存在的其他模型。您应该拥有对包含所有模型的主集合的引用,而不考虑搜索。此“主集合”本身不与视图相关联,但您可以使用此主集合上的过滤器,并使用过滤后的模型更新视图的集合

至于第二个问题,您不应该从模型中引用视图。模型应该完全独立于视图-只有视图应该引用模型

您的
addOne
方法可以这样重构以获得更好的性能(始终使用$el附加子视图):


我玩弄你的问题时有点得意忘形

首先,我将创建一个专用集合来保存过滤后的模型,并创建一个“状态模型”来处理搜索。比如说,

var Filter = Backbone.Model.extend({
    defaults: {
        what: '', // the textual search
        where: 'all' // I added a scope to the search
    },
    initialize: function(opts) {
        // the source collection
        this.collection = opts.collection; 
        // the filtered models
        this.filtered = new Backbone.Collection(opts.collection.models); 
        //listening to changes on the filter
        this.on('change:what change:where', this.filter); 
    },

    //recalculate the state of the filtered list
    filter: function() {
        var what = this.get('what').trim(),
            where = this.get('where'),
            lookin = (where==='all') ? ['first', 'last'] : where,
            models;

        if (what==='') {
            models = this.collection.models;            
        } else {
            models = this.collection.filter(function(model) {
                return _.some(_.values(model.pick(lookin)), function(value) {
                    return ~value.toLowerCase().indexOf(what);
                });
            });
        }

        // let's reset the filtered collection with the appropriate models
        this.filtered.reset(models); 
    }
});
它将被实例化为

var people = new Backbone.Collection([
    {first: 'John', last: 'Doe'},
    {first: 'Mary', last: 'Jane'},
    {first: 'Billy', last: 'Bob'},
    {first: 'Dexter', last: 'Morgan'},
    {first: 'Walter', last: 'White'},
    {first: 'Billy', last: 'Bobby'}
]);
var flt = new Filter({collection: people});
然后,我将为列表和输入字段创建单独的视图:更易于维护和移动

var BaseView = Backbone.View.extend({
    render:function() {
        var html, $oldel = this.$el, $newel;

        html = this.html();
        $newel=$(html);

        this.setElement($newel);
        $oldel.replaceWith($newel);

        return this;
    }
});
var CollectionView = BaseView.extend({
    initialize: function(opts) {
        // I like to pass the templates in the options
        this.template = opts.template;
        // listen to the filtered collection and rerender
        this.listenTo(this.collection, 'reset', this.render);
    },
    html: function() {
        return this.template({
            models: this.collection.toJSON()
        });
    }
});
var FormView = Backbone.View.extend({
    events: {
        // throttled to limit the updates
        'keyup input[name="what"]': _.throttle(function(e) {
             this.model.set('what', e.currentTarget.value);
        }, 200),

        'click input[name="where"]': function(e) {
            this.model.set('where', e.currentTarget.value);
        }
    }
});
BaseView
允许就地更改DOM,有关详细信息,请参阅

这些实例看起来像

var inputView = new FormView({
    el: 'form',
    model: flt
});
var listView = new CollectionView({
    template: _.template($('#template-list').html()),
    collection: flt.filtered
});
$('#content').append(listView.render().el);
以及现阶段的搜索演示

最后,我将修改
CollectionView
以移植渲染函数中的行视图,类似于

var ItemView = BaseView.extend({
    events: {
        'click': function() {
            console.log(this.model.get('first'));
        }
    }
});

var CollectionView = BaseView.extend({
    initialize: function(opts) {
        this.template = opts.template;
        this.listenTo(this.collection, 'reset', this.render);
    },
    html: function() {
        var models = this.collection.map(function (model) {
            return _.extend(model.toJSON(), {
                cid: model.cid
            });
        });
        return this.template({models: models});
    },
    render: function() {
        BaseView.prototype.render.call(this);

        var coll = this.collection;
        this.$('[data-cid]').each(function(ix, el) {
            new ItemView({
                el: el,
                model: coll.get($(el).data('cid'))
            });
        });

        return this;
    }
});

又是一把小提琴

谢谢你的回复。第一个问题。。。
this.listenTo(this.collection,“reset”,this.render)
this.collection.on(“reset”,this.render,this)
之间有什么区别?第二个问题。我喜欢你说的关于主收藏和收藏视图收藏。。。但是您没有处理子视图。使用每个
addOne
创建新的
RowView
s可以吗?@savinger它们基本上完成了相同的任务—它们侦听事件。但是,
this.listenTo
将侦听与视图相关联,而
this.collection.on
将侦听与集合相关联。这似乎没有什么区别,但请记住,如果使用
此.collection.on
,即使删除视图,该集合仍将继续侦听,这可能会导致内存泄漏并大大降低应用程序的速度。另一方面,如果使用
this.listenTo
,则在删除视图后,它将不会侦听事件。不,不会出现内存泄漏。但是,您可能需要考虑使用<代码> SET>代码>而不是<代码> RESET>代码>。当重置清除现有模型并渲染新模型时,set将执行“智能合并”,该“智能合并”将“更新”视图并仅删除需要删除的模型-这可能更有效。作为旁注,如果您知道要调用
this.$('something')
那么多次可能会更好地缓存该选择器,对吗?比如
var$foo=this.$('foo')见此:谢谢,这非常有帮助。我真的很喜欢你所做的
var ItemView = BaseView.extend({
    events: {
        'click': function() {
            console.log(this.model.get('first'));
        }
    }
});

var CollectionView = BaseView.extend({
    initialize: function(opts) {
        this.template = opts.template;
        this.listenTo(this.collection, 'reset', this.render);
    },
    html: function() {
        var models = this.collection.map(function (model) {
            return _.extend(model.toJSON(), {
                cid: model.cid
            });
        });
        return this.template({models: models});
    },
    render: function() {
        BaseView.prototype.render.call(this);

        var coll = this.collection;
        this.$('[data-cid]').each(function(ix, el) {
            new ItemView({
                el: el,
                model: coll.get($(el).data('cid'))
            });
        });

        return this;
    }
});