简体   繁体   English

Backbone.js中的集合不会触发事件

[英]Collection in Backbone.js Not Firing Events

I am trying to implement an action when a change to a model in a given collection is fired, but whenever I try to put a listener on the change , add , or remove events on my collection, no events are fired. 我试图在触发给定集合中的模型更改时实现操作,但每当我尝试将侦听器放在我的集合上的changeaddremove事件时,都不会触发任何事件。 When I attached a change event to a model's initialize function (just to test), the change event was fired successfully. 当我将更改事件附加到模型的initialize函数(仅用于测试)时, change事件已成功触发。 The view below doesn't itself change the code, but I want it to re-render itself whenever a change to the collection has been made. 下面的视图本身不会更改代码,但我希望每当对集合进行更改时它都会重新呈现。 It could be something wrong with my setup, but I want to get your advice. 我的设置可能有问题,但我想得到你的建议。 Below is my code: 以下是我的代码:

contact.js (Model): contact.js(型号):

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

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

  return ContactModel;
});

contacts.js (Collection): contacts.js(收藏):

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

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

  return ContactCollection;

});

contactlist.js (View): 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;

});

Since Backbone 1.0 ( change log ), the answer to this question isn't 100% accurate anymore. 自从Backbone 1.0( 更改日志 )以来,这个问题的答案不再是100%准确的。

In Backbone 1.0+, you'll have to pass { reset: true } into the .fetch() method if you want the reset event to fire. 在Backbone 1.0+中,如果要.fetch() reset事件,则必须将{ reset: true }传递给.fetch()方法。 See below. 见下文。

Calling fetch on a collection will now trigger a 3 different events: 在集合上调用fetch现在将触发3个不同的事件:


collection.fetch(); //triggers the following events
  1. request : Triggered before the ajax request is made request :在发出ajax请求之前触发
  2. add : (multiple times) this event will be triggered multiple times as each item in the data is added to the collection. add :(多次)当数据中的每个项目添加到集合中时,将多次触发此事件。
  3. sync : Triggered after data on the collection is synced with the data provider (data added to collection, ajax request completed) sync :在集合上的数据与数据提供程序同步后触发(数据已添加到集合,ajax请求已完成)

To revert to the old style of backbone events, you need to call 要恢复到旧式的骨干事件,您需要调用

collection.fetch({ reset: true }); //triggers the following events
  1. request : Triggered before the ajax request is made request :在发出ajax请求之前触发
  2. reset : Triggered when all data is placed into the collection. reset :将所有数据放入集合时触发。
  3. sync : Triggered after data on the collection is synced with the data provider (data added to collection, ajax request completed) sync :在集合上的数据与数据提供程序同步后触发(数据已添加到集合,ajax请求已完成)

The best way to quickly see what events that your collection is triggering , is to bind to the all event and log the arguments: 快速查看集合触发的事件的最佳方法是绑定到all事件并记录参数:

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

When running fetch() , the collection will trigger it's reset event, not add . 运行fetch() ,集合将触发它的reset事件,而不是add

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

That will run as soon as the fetch completes, before your success callback. success回调之前,一旦获取完成,它就会运行。

change runs when the model itself has attributes changed, which you don't do anywhere in the code you posted. 当模型本身更改了属性时, change将运行,您在发布的代码中不会执行任何操作。

Also generally I wouldn't access .models directly. 通常我也不会直接访问.models I'd do this: 我这样做:

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

Update 更新

As it is now, you create the collection when you render. 就像现在一样,您在渲染时创建集合。 This is not a good idea because ideally you would want to be able to render again without fetching all of the data again. 这不是一个好主意,因为理想情况下,您希望能够再次渲染而无需再次获取所有数据。

I would move the collection creation logic into the view's initialize function like this: 我将集合创建逻辑移动到视图的initialize函数中,如下所示:

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

That way, as the collection changes, render is automatically triggered. 这样,随着集合的更改,渲染会自动触发。 This is complicated by your options , so I would try to change how you pass those arguments in. Maybe add a 'setOptions' function that saves the options to use while rendering. 你的options很复杂,所以我会尝试改变你传递这些参数的方式。也许添加一个'setOptions'函数来保存渲染时使用的选项。 Ideally render will re-render the page identically to how it was before the call, it doesn't normally have arguments. 理想情况下, render将重新呈现页面与调用之前的相同,它通常不具有参数。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM