【问题标题】:Collection in Backbone.js Not Firing EventsBackbone.js 中的集合不触发事件
【发布时间】:2012-12-30 20:50:48
【问题描述】:

我试图在给定集合中的模型更改被触发时执行一个操作,但是每当我尝试在我的集合中的changeaddremove 事件上放置一个侦听器时,没有事件被触发。当我将更改事件附加到模型的 initialize 函数(只是为了测试)时,change 事件被成功触发。下面的视图本身不会更改代码,但我希望它在对集合进行更改时重新呈现自己。我的设置可能有问题,但我想听听你的建议。以下是我的代码:

contact.js(模型):

define([
  'jquery',
  'underscore',
  'backbone'
], function($, _, Backbone){

  var ContactModel = Backbone.Model.extend({
        urlRoot: '/contacts'
    });

  return ContactModel;
});

contacts.js(集合):

define([
  'jquery',
  'underscore',
  'backbone',
  'models/contact'
], function($, _, Backbone, Contact){

  var ContactCollection = Backbone.Collection.extend({
    url: '/contacts',
    model: Contact
  });

  return ContactCollection;

});

contactlist.js(查看):

define([
  'jquery',
  'underscore',
  'backbone',
  'text!templates/contactlist.html',
  'collections/contacts'
], function($, _, Backbone, contactListTemplate, Contacts){

  var ContactListView = Backbone.View.extend({
    el: '.contact-list',
    render: function(options) {
        var that = this;
        var contacts = new Contacts();
        contacts.on("add", function() {
            console.log('change');
        });
        contacts.fetch({
            success: function(contacts) {
                var results = contacts.models;
                if (options.query || options.query === '') {
                    var results = [];
                    var query = options.query.toUpperCase();
                    for (var contact in contacts.models) {
                        if (contacts.models[contact].get('firstname').toUpperCase().indexOf(query) !== -1 ||
                                contacts.models[contact].get('lastname').toUpperCase().indexOf(query) !== -1 ||
                                contacts.models[contact].get('email').toUpperCase().indexOf(query) !== -1 ||
                                contacts.models[contact].get('phonenumber').toString().toUpperCase().indexOf(query) !== -1)
                        {
                            results.push(contacts.models[contact]);
                        }
                    }
                    options.idclicked = null;
                }
                if (!options.idclicked && results[0]) {
                    options.idclicked = results[0].get('id');
                    Backbone.history.navigate('/contacts/' + results[0].get('id'), {trigger: true});
                }
                var template = _.template(contactListTemplate, {contacts: results, idclicked: options.idclicked});
                that.$el.html(template);
                $(document).ready(function() {
                    $('.contact-list').scrollTop(options.scrolled);
                });
            }
        });
    },
    events: {
        'click .contact': 'clickContact'
    },
    clickContact: function (ev) {
        $('.contact-clicked').removeClass('contact-clicked').addClass('contact');
        $(ev.currentTarget).addClass('contact-clicked').removeClass('contact');
        window.location.replace($(ev.currentTarget).children('a').attr('href'));
    }
  });

  return ContactListView;

});

【问题讨论】:

    标签: javascript backbone.js backbone-events


    【解决方案1】:

    自 Backbone 1.0 (change log) 以来,这个问题的答案不再是 100% 准确。

    在 Backbone 1.0+ 中,如果要触发 reset 事件,则必须将 { reset: true } 传递给 .fetch() 方法。见下文。

    在集合上调用 fetch 现在将触发 3 个不同的事件:


    collection.fetch(); //triggers the following events
    
    1. request: ajax 请求前触发
    2. add:(多次)此事件将被触发多次,因为每个项目 数据被添加到集合中。
    3. sync:集合上的数据与 数据提供者(数据添加到集合,ajax 请求完成)

    要恢复到旧式主干事件,您需要调用

    collection.fetch({ reset: true }); //triggers the following events
    
    1. request: ajax 请求前触发
    2. reset:将所有数据放入集合时触发。
    3. sync:集合上的数据与 数据提供者(数据添加到集合,ajax 请求完成)

    快速查看您的集合正在触发的事件的最佳方法是绑定到all 事件并记录参数:

    collection.on('all', function() {
        console.log(arguments);
    });
    

    【讨论】:

      【解决方案2】:

      当运行fetch() 时,集合将触发它的reset 事件,而不是add

      contacts.on("reset", function() {
          console.log('reset');
      });
      

      这将在获取完成后立即运行,在您的 success 回调之前。

      change 在模型本身的属性发生更改时运行,而您在发布的代码中的任何地方都不会这样做。

      通常我也不会直接访问.models。我会这样做:

      var results = contacts.toArray();
      if (options.query || options.query === ''){
        results = contacts.filter(function(contact){
          return _.any(['firstname', 'lastname', 'email', 'phonenumber'], function(attr){
            return contact.get(attr).toString().toUpperCase().indexOf(query) !== -1;
          });
        }); 
      // ...
      

      更新

      就像现在一样,您在渲染时创建集合。这不是一个好主意,因为理想情况下,您希望能够再次渲染而无需再次获取所有数据。

      我会将集合创建逻辑移动到视图的initialize 函数中,如下所示:

      initialize: function(options){
        this.collection = new Contacts();
        this.collection.on('reset add remove', this.render, this);
      }
      

      这样,随着集合的变化,渲染会自动触发。你的options 让这很复杂,所以我会尝试改变你传递这些参数的方式。也许添加一个“setOptions”函数来保存渲染时使用的选项。理想情况下,render 会按照调用前的方式重新渲染页面,它通常没有参数。

      【讨论】:

      • 我试图通过编辑我的问题来澄清我想要在上面实现的目标。该建议有效,但我不确定它是否满足我想要做的事情。我应该提供任何其他信息吗?对不起,如果我打扰了你。
      • @BradRoss 已更新。希望这有助于为您指明正确的方向。
      • 我认为确实如此。关于不再获取所有数据的评论,在重构我的应用程序时我应该在概念上尝试做什么?很抱歉打扰,但最好在我养成任何坏习惯之前学习最佳做法。
      • @BradRoss Backbone 如此受欢迎的原因之一是它非常开放,所以很多事情都只是你的电话。我会专注于将数据和表示分开,并尽量不要让事情过于异步。所以在我的例子中,渲染是一个基于现有集合的同步过程,它不需要知道数据来自哪里或如何变化。您之前的代码将这两者交织在一起,使其更加复杂。
      • 我想了解更多关于这个和你关于每次渲染时不获取数据的评论,但我不想打扰你。有没有我可以阅读的教程/文章/书籍可以帮助我更多地理解和实施这个概念?
      猜你喜欢
      • 2011-12-31
      • 1970-01-01
      • 2012-06-07
      • 2011-07-12
      • 1970-01-01
      • 1970-01-01
      • 2012-04-16
      • 2014-01-03
      • 1970-01-01
      相关资源
      最近更新 更多