Javascript Backbone.js中的集合未触发事件

Javascript Backbone.js中的集合未触发事件,javascript,backbone.js,backbone-events,Javascript,Backbone.js,Backbone Events,当对给定集合中的模型进行更改时,我试图实现一个操作,但每当我尝试在集合中的change、add或remove事件上放置侦听器时,都不会触发任何事件。当我将更改事件附加到模型的initialize函数(仅用于测试)时,change事件成功触发。下面的视图本身不会更改代码,但我希望它在对集合进行更改时重新呈现自身。我的设置可能有问题,但我想听听你的建议。下面是我的代码: contact.js(型号): define([ 'jquery', 'underscore', 'backbone'

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

contact.js(型号):

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

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

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

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

  return ContactCollection;

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

});
contacts.js(收藏):

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

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

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

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

  return ContactCollection;

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

});
contactlist.js(视图):

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

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

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

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

  return ContactCollection;

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

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

contacts.on("reset", function() {
    console.log('reset');
});
成功
回调之前,它将在提取完成后立即运行

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);
}
这样,随着集合的更改,渲染将自动触发。由于您的
选项
,这很复杂,因此我将尝试更改传入这些参数的方式。可以添加一个“setOptions”函数,保存渲染时要使用的选项。理想情况下,
render
将重新呈现与调用前相同的页面,它通常没有参数。

由于主干1.0(),这个问题的答案不再100%准确

在主干网1.0+中,如果希望触发
reset
事件,则必须将
{reset:true}
传递到
.fetch()
方法中。见下文

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


  • 请求
    :在发出ajax请求之前触发
  • add
    :(多次)此事件将在中的每个项目都被触发多次 数据将添加到集合中
  • sync
    :在集合上的数据与 数据提供程序(数据添加到集合中,ajax请求已完成)

  • 要恢复到旧的主干事件样式,需要调用

    collection.fetch({ reset: true }); //triggers the following events
    
  • 请求
    :在发出ajax请求之前触发
  • 重置
    :将所有数据放入集合时触发
  • sync
    :在集合上的数据与 数据提供程序(数据添加到集合中,ajax请求已完成)

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

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

    我试图通过编辑我的问题来澄清我想要达到的目的。这个建议奏效了,但我不确定它是否满足了我的愿望。我应该提供其他信息吗?“对不起,我打扰你了。”布拉德罗斯更新。希望这能帮你指明正确的方向。我想是的。关于关于不再获取所有数据的评论,在重新构建我的应用程序时,在概念上我应该尝试做什么?很抱歉打扰您,但在我养成任何坏习惯之前学习最佳实践是很好的。@BradRoss主干网之所以如此受欢迎,一个原因是它非常开放,所以很多事情都是由您决定的。我会把重点放在保持数据和表示分离上,尽量不要使事情过于异步。因此,在我的示例中,渲染是基于现有集合的同步过程,它不需要知道数据来自何处或如何更改。您以前的代码将这两个代码交织在一起,使其更加复杂。我想进一步了解这一点,以及您关于不在每次渲染时获取数据的评论,但我不想再打扰您了。有没有教程/文章/书籍可以帮助我更好地理解和实施这个概念?