Ответ 1
Вы также можете использовать хороший, оловянный pop destroy-in-place:
var model;
while (model = this.collection.first()) {
model.destroy();
}
С первой попытки я написал
this.collection.each(function(element){
element.destroy();
});
Это не работает, потому что он похож на ConcurrentModificationException
на Java, где удаляются все остальные элементы.
Я попробовал привязать "удалить" событие на модели, чтобы уничтожить себя, как было предложено Уничтожение модели Backbone в коллекции за один шаг?, но это приведет к удалению 2 delete запросы, если я вызываю destroy на модели, принадлежащей коллекции.
Я посмотрел на underscore doc и не вижу вариант each()
, который зацикливается назад, что решит проблему удаления каждого элемента.
Что бы вы предложили как самый чистый способ уничтожить коллекцию моделей?
Спасибо
Вы также можете использовать хороший, оловянный pop destroy-in-place:
var model;
while (model = this.collection.first()) {
model.destroy();
}
Недавно я столкнулся с этой проблемой. Похоже, вы разрешили его, но я думаю, что более подробное объяснение может также быть полезным для других, которые задаются вопросом, почему именно это происходит.
Предположим, что у нас есть коллекция (библиотека) моделей (книг).
Например:
console.log(library.models); // [object, object, object, object]
Теперь давайте рассмотрим и удалим все книги, используя ваш первоначальный подход:
library.each(function(model) {
model.destroy();
});
each
- это метод подчеркивания, который перемещается в коллекцию Backbone. Он использует ссылку на коллекции для своих моделей (library.models
) в качестве аргумента по умолчанию для этих различных методов подбора подчеркивания. Да конечно. Это звучит разумно.
Теперь, когда модель вызывает destroy
, она запускает событие "уничтожить" в коллекции, а затем удалить ссылку на модель. Внутри remove
вы заметите следующее:
this.models.splice(index, 1);
Если вы не знакомы с splice
, см. doc. Если да, то вы можете понять, почему это проблематично.
Просто чтобы продемонстрировать:
var list = [1,2];
list.splice(0,1); // list is now [2]
Это приведет к тому, что цикл each
пропустит элементы, потому что его ссылка на объекты модели через models
динамически изменяется!
Теперь, если вы используете JavaScript < 1.6, то вы можете столкнуться с этой ошибкой:
Uncaught TypeError: Cannot call method 'destroy' of undefined
Это связано с тем, что в реализации подчёркивания each
в нижней подчеркивании он возвращается к своей собственной реализации, если отсутствует родной forEach
. Он жалуется, что вы удаляете среднюю итерацию элемента, потому что она по-прежнему пытается получить доступ к несуществующим элементам.
Если нативный forEach
существует, тогда он будет использоваться вместо этого, и вы не получите ошибку вообще!
Почему? Согласно doc:
Если существующие элементы массива будут изменены или удалены, их значение, переданное обратному вызову, будет значением в момент, когда каждый посетит их; удаленные элементы не посещаются.
Итак, какое решение?
Не используйте collection.each
, если вы удаляете модели из коллекции. Используйте метод, который позволит вам работать с новым массивом, содержащим ссылки на модели. Один из способов - использовать метод подчеркивания clone
.
_.each(_.clone(collection.models), function(model) {
model.destroy();
});
Я немного опоздал, но я думаю, что это тоже довольно сжатое решение:
_.invoke(this.collection.toArray(), 'destroy');
Ответ на вопрос Шона Андерсона. Существует прямой доступ к массиву сборных матриц, поэтому вы можете сделать это следующим образом.
_.invoke(this.collection.models, 'destroy');
Или просто вызовите reset()
в коллекции без параметров, метод destroy
на моделях в этой коллекции будет активирован bi.
this.collection.reset();
Это работает, удивляясь, что я не могу использовать подчеркивание для этого.
for (var i = this.collection.length - 1; i >= 0; i--)
this.collection.at(i).destroy();
Я предпочитаю этот метод, особенно если вам нужно вызвать destroy на каждой модели, очистить коллекцию и не вызывать DELETE
на сервер. Удаление параметра id
или любого другого idAttribute
установлено, что позволяет это.
var myCollection = new Backbone.Collection();
var models = myCollection.remove(myCollection.models);
_.each(models, function(model) {
model.set('id', null); // hack to ensure no DELETE is sent to server
model.destroy();
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="http://underscorejs.org/underscore-min.js"></script>
<script src="http://backbonejs.org/backbone-min.js"></script>
Для этого вам не нужно подчеркивание и цикл for
.
this.collection.slice().forEach(element => element.destroy());