Сортировка Backbone Collection в обработчике событий Add повторно запускает событие Add.

У меня есть коллекция, которая сортируется на основе атрибута модели, иногда я хочу добавить модель и просто вычислить порядок в обработчике событий add. Проблема в том, что когда я вызываю collection.sort() в обработчике события добавления, по какой-то причине он снова запускает событие добавления.

Поскольку мое событие добавления выполняет некоторые вставки DOM, я получаю дубликаты элементов в своей DOM.

Пока единственное решение, которое я нашел, - это вычислить следующий порядок перед добавлением модели или вызовом _.defer и сортировкой коллекции, а затем запуском любого дальнейшего кода.

См. полную скрипту: http://jsfiddle.net/DD23n/9/


person Clarence Liu    schedule 07.06.2012    source источник
comment
Ах событий бури! Нежное хлопанье умирающего программного обеспечения. Дизайн Backbone имеет высокую степень этого из-за того, как он спроектирован. В основном чрезмерное использование привязок событий для присоединения всех форм логики.   -  person chubbsondubs    schedule 07.06.2012
comment
@chubbard: Это не шторм событий, это я меняю массив, который повторяю во время шторма.   -  person mu is too short    schedule 08.06.2012
comment
Вы лукавите (может быть, в шутку, но все же это тривиальное возражение). Изменение массива отправляет событие, которое запускает дополнительную логику, которая изменяет массив, отправляет больше событий и т. д. Все, что вызвано автоматической диспетчеризацией событий и привязкой, заблокированной мертвой хваткой, удерживает эту магистраль, которая рада предложить в качестве наилучшей практики.   -  person chubbsondubs    schedule 08.06.2012
comment
@chubbard: Нет, это вовсе не проблема событий, запускающих события, даже близко. Проблема, в частности, в том, что Клей модифицировал некоторые из них, которые в настоящее время зацикливались, тот факт, что диспетчер событий выполнял зацикливание, совершенно не имеет значения. То, что вам не нравятся системы, основанные на событиях, не означает, что они всегда виноваты. Мое исправление было именно таким: исправлением вашего ошибочного комментария.   -  person mu is too short    schedule 08.06.2012
comment
Мне не нравится то, как Backbone выступает за использование событий, или отсутствие каких-либо указаний, потому что я думаю, что это создает подобный беспорядок. Это не системы, основанные на событиях в целом. Не думайте, что я предвзят.   -  person chubbsondubs    schedule 08.06.2012


Ответы (2)


sntran правильно говорит о том, откуда возникла ваша проблема: вы изменяете массив во время его повторения.

Вы начинаете с новой модели с nOrder: null в начале списка моделей. :

splice.apply(this.models, [index, 0].concat(models));

затем add перебирает модели, запуская 'add' события по ходу дела. :

for (i = 0, length = this.models.length; i < length; i++) {
  if (!cids[(model = this.models[i]).cid]) continue;
  options.index = i;
  model.trigger('add', model, this, options);
}

Но внутри обратного вызова 'add' вы изменяете модель:

if(model.get('nOrder') == null)
    model.set('nOrder', _.max(collection.pluck('nOrder')) + 1);

а затем отсортировать коллекцию:

collection.sort({silent: true});

Эти два действия перемещают this.models[0] в this.models[3] в цикле запуска событий; но i для этого цикла просто будет продолжать тикать, а новый this.models[3] (который раньше был 0) снова пройдет тест if (!cids[(model = this.models[i]).cid]), и будет ваше второе событие 'add'.

Вы можете посмотреть эту версию вашей работы со скрипкой, чтобы увидеть, как массив коллекции меняется за вашей спиной:

http://jsfiddle.net/ambiguous/p8Fp4/

Я думаю, что самое простое решение — добавить в вашу коллекцию метод append, который устанавливает соответствующее значение nOrder в модели, а затем добавляет его в коллекцию:

append: function(m) {
    var nOrder = _.max(this.pluck('nOrder')) + 1;
    if(m instanceof Backbone.Model)
        m.set({ nOrder: nOrder });
    else
        m.nOrder = nOrder;
    this.add(m);
}

Затем ваш обратный вызов 'add' может оставить nOrder в покое и прекратить сортировку коллекции.

Демонстрация: http://jsfiddle.net/ambiguous/JqWVP/

Вы также можете переопределить метод коллекции add, но это намного сложнее, если вы хотите сделать это правильно.

person mu is too short    schedule 07.06.2012
comment
Спасибо, это отличное решение - я провел еще несколько тестов, и это, безусловно, своего рода уродливое состояние гонки. Первоначально я думал, что когда я вызывал sort из обработчика добавления, он запускал события добавления для управления коллекцией, не понимая, что это был исходный цикл добавления. Веселье - person Clarence Liu; 08.06.2012

Если вы прочитаете исходный код backbone, функция add объединяет модель вставки в collection.models. В этом случае у вас есть четыре.

Поскольку вы не указали функции add на молчание, она проходит через collection.models и запускает событие 'add' для вставляемого элемента.

Но вы вызываете sort в обработчике add, collection.models становится другим. Когда коллекция выполняет свой цикл в функции add, вставляемая модель будет в другой позиции, и она снова вызовет «добавить».

Я не уверен, что это имеет смысл, но я думаю, что лучше не переупорядочивать/модифицировать массив во время цикла.

person Sơn Trần-Nguyễn    schedule 07.06.2012
comment
Но если add молчит, обработчик вообще не будет вызываться. Я думаю, что реальным решением было бы установить nOrder перед добавлением модели, чтобы set и order не понадобились в обработчике событий 'add'. - person mu is too short; 08.06.2012
comment
Я тоже так думаю. Если установить его перед добавлением, коллекция будет сортироваться сама по себе, не затрагивая промежуточную коллекцию. - person Sơn Trần-Nguyễn; 08.06.2012