Коллекция в Backbone.js, не вызывающая события

Я пытаюсь реализовать действие, когда запускается изменение модели в данной коллекции, но всякий раз, когда я пытаюсь поместить прослушиватель событий change, add или remove в моей коллекции, никакие события не запускаются. Когда я прикрепил событие изменения к функции 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;

});

person Brad Ross    schedule 30.12.2012    source источник


Ответы (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 будет перерисовывать страницу идентично тому, как она была до вызова, обычно она не имеет аргументов.

person loganfsmyth    schedule 30.12.2012
comment
Я попытался уточнить, чего я хочу достичь выше, отредактировав свой вопрос. Предложение сработало, но я не уверен, что оно соответствует тому, что я хочу сделать. Должен ли я предоставить какую-либо другую информацию? Извините, если я вам мешаю. - person Brad Ross; 31.12.2012
comment
@BradRoss Обновлено. Надеюсь, что это поможет указать вам в правильном направлении. - person loganfsmyth; 31.12.2012
comment
Я думаю, что да. Что касается комментария о невозможности повторного получения всех данных, что концептуально я должен попытаться сделать при реструктуризации своего приложения? Извините за беспокойство, но полезно изучить лучшие практики, прежде чем у меня появятся какие-либо вредные привычки. - person Brad Ross; 31.12.2012
comment
@BradRoss Одна из вещей, которая делает Backbone настолько популярным, заключается в том, что он чрезвычайно открыт, поэтому многое зависит только от вас. Я бы сосредоточился на разделении данных и представления и старался не делать вещи слишком асинхронными. Итак, в моем примере рендеринг — это синхронный процесс, основанный на существующей коллекции, ему не нужно ничего знать о том, откуда взялись эти данные или как они меняются. Ваш предыдущий код переплел эти два, что сделало его более сложным. - person loganfsmyth; 31.12.2012
comment
Я хочу узнать больше об этом и вашем комментарии о том, что данные не извлекаются каждый раз при рендеринге, но я не хочу вас больше беспокоить. Есть ли учебник/статья/книга, которые я могу прочитать, что поможет мне лучше понять и реализовать эту концепцию? - person Brad Ross; 31.12.2012

Начиная с Backbone 1.0 (журнал изменений), ответ на этот вопрос больше не является точным на 100%.

В Backbone 1.0+ вам нужно передать { reset: true } в метод .fetch(), если вы хотите, чтобы событие reset срабатывало. Увидеть ниже.

Вызов 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);
});
person Cory Danielson    schedule 07.04.2013