После первоначального знакомства с представлениями и моделями Backbone мы рассмотрим следующую фундаментальную сущность Backbone.js — Collection . Коллекции представляют собой упорядоченный набор моделей и становятся очень удобными для любого типа приложений. Учтите, что мы почти всегда оперируем множеством разных моделей: постами, твитами, новостями и т. Д. — все это коллекции, обычно представляемые в виде списков или сеток.
В небольшом приложении мы делаем через эту серию у нас есть коллекция отзывов. Но прежде чем я покажу, как интегрировать коллекцию в приложение, я хочу убедиться, что вы правильно понимаете все свойства коллекции. Мы сделаем это с помощью TDD, исследуя коллекцию и исследуя ее поведение.
Сборка конструкций
Итак, для создания коллекции нам нужно расширить Backbone.Collection
объект,
var FeedbackCollection = Backbone.Collection.extend({ model: Feedback, url: '/feedback' });
Здесь мы только что указали URL для сохранения коллекции и модель, из которой будет состоять коллекция. Давайте подготовим тестовый набор для сборника историй,
describe('FeedbackCollection.js spec', function () { var collection; });
И создайте нашу первую спецификацию,
describe('when constructing', function () { describe('just empty', function () { beforeEach(function () { collection = new FeedbackCollection(); }); it('should be created', function () { expect(collection).toBeDefined(); }); });
Просто чтобы убедиться, что наше определение в порядке, и мы можем создать экземпляр нового объекта коллекции.
FeedbackCollection
Конструктор имеет несколько дополнительных аргументов — models
, options
. Модели, могут быть либо массивами объектов, либо массивом Backbone.Models. В случае объекта конструктор коллекции «превратит» их в модели (принимая тип, который мы указали в определении коллекции) и добавит эти модели в коллекцию.
или же,
describe('with models', function () { beforeEach(function () { var models = [ new Feedback({email: '[email protected]', website: 'a.com', feedback: 'hello'}), new Feedback({email: '[email protected]', website: 'b.com', feedback: 'good bye'}) ]; collection = new FeedbackCollection(models); }); it('should be lenght of 2', function () { expect(collection.length).toBe(2); }); it('should contain models inside', function () { expect(collection.models).toBeDefined(); }); });
Обе вещи эквивалентны. Более того, обычно для инициализации коллекций вам нужны только юнит-тесты, поэтому я обычно предпочитаю первый вариант.
options
Параметр может содержать тип модели, коллекции содержат. Итак, если в коллекции не указано model
свойство, Backbone.Model
будет создано по умолчанию.
describe('with options', function () { beforeEach(function () { var models = [ {email: '[email protected]', website: 'a.com', feedback: 'hello'}, {email: '[email protected]', website: 'b.com', feedback: 'good bye'} ]; collection = new Backbone.Collection(models); // not specifying model }); it('should be created', function () { expect(collection).toBeDefined(); }); it('should have models of Backbone.Model type', function () { expect(collection.models[0].constructor).toBe(Backbone.Model); });
Вы можете переопределить это, передав {model: MyModel}
объект параметров,
describe('while passing model option', function () { beforeEach(function () { var models = [ {email: '[email protected]', website: 'a.com', feedback: 'hello'}, {email: '[email protected]', website: 'b.com', feedback: 'good bye'} ]; collection = new Backbone.Collection(models, { model: Feedback }); }); it('should have models of Feedback type', function () { expect(collection.models[0].constructor).toBe(Feedback); }); });
Несмотря на такую возможность, я редко использую это на практике. Лучше просто указать тип модели в определении коллекции, что облегчает понимание кода.
Доступ к элементам коллекции
После создания коллекции можно получить доступ к внутренним моделям. Есть несколько способов сделать это.
Самый простой по индексу,
describe('when accessing collection elements', function () { var first, second, models; describe('by index', function () { beforeEach(function () { models = [ {email: '[email protected]', website: 'a.com', feedback: 'hello'}, {email: '[email protected]', website: 'b.com', feedback: 'good bye'} ]; collection = new FeedbackCollection(models); }); beforeEach(function () { first = collection.at(0); second = collection.at(1); }); it('should get first model by index', function () { expect(first.toJSON()).toEqual(models[0]); }); it('should get second model by index', function () { expect(second.toJSON()).toEqual(models[1]); }); });
Даже если это возможно, в реальных приложениях вы, вероятно, не знаете индекс модели, который нужно получить из коллекции, поскольку они могут поступать с сервера в непредсказуемом порядке. Таким образом, вместо индекса более подходящим является получение по id.
describe('by id', function () { beforeEach(function () { models = [ {id: 'feedback-1', email: '[email protected]', website: 'a.com', feedback: 'hello'}, {id: 'feedback-2', email: '[email protected]', website: 'b.com', feedback: 'good bye'} ]; collection = new FeedbackCollection(models); }); beforeEach(function () { first = collection.get('feedback-1'); second = collection.get('feedback-2'); }); it('should get first model by id', function () { expect(first.toJSON()).toEqual(models[0]); }); it('should get second model by id', function () { expect(second.toJSON()).toEqual(models[1]); }); });
И, наконец, то, что я поймал в ловушку много раз, когда запускал Backbone — индексаторы на коллекции, не работает.
describe('indexer does not work', function () { beforeEach(function () { models = [ {id: 'feedback-1', email: '[email protected]', website: 'a.com', feedback: 'hello'}, {id: 'feedback-2', email: '[email protected]', website: 'b.com', feedback: 'good bye'} ]; collection = new FeedbackCollection(models); }); it('should be undefined', function () { expect(collection[0]).not.toBeDefined(); }); });
Добавление и удаление предметов
Далее нам нужно понять, как добавлять и удалять элементы из коллекций.
Есть 2 способа добавления элемента в коллекцию магистральной: add
, push
. Они очень похожи, но между ними есть разница. Метод add берет модель или массив моделей и опции, которые вы можете указать позицию, к которой должен быть представлен элемент. Метод Push, просто добавит новый элемент в конец коллекции.
describe('by add method', function () { beforeEach(function () { collection.add({id: 'feedback-1', email: '[email protected]', website: 'a.com', feedback: 'hello'}); }); it('should be added', function () { expect(collection.get('feedback-1')).toBeDefined(); }); it('should be converted to model', function () { expect(collection.get('feedback-1').constructor).toBe(Feedback); }); describe('with index specified', function () { beforeEach(function () { collection.add({id: 'feedback-2', email: '[email protected]', website: 'b.com', feedback: 'good bye'}, {at: 0}); }); it('should have 2 items in collection', function () { expect(collection.length).toBe(2); }); it('should have feedback-2 item at index 0', function () { expect(collection.at(0).id).toBe('feedback-2'); }); }); });
Толчком,
describe('by push method', function () { beforeEach(function () { collection.push({id: 'feedback-1', email: '[email protected]', website: 'a.com', feedback: 'hello'}); }); it('should be added', function () { expect(collection.get('feedback-1')).toBeDefined(); }); it('should be converted to model', function () { expect(collection.get('feedback-1').constructor).toBe(Feedback); }); describe('with next push', function () { beforeEach(function () { collection.push({id: 'feedback-2', email: '[email protected]', website: 'b.com', feedback: 'good bye'}); }); it('should have 2 items in collection', function () { expect(collection.length).toBe(2); }); it('should have feedback-1 item at index 0', function () { expect(collection.at(0).id).toBe('feedback-1'); }); }); });
Обратите внимание, что он push
получает те же параметры, что и add
, но это просто сокращение для add
метода (посмотрите, как он реализован , чтобы сделать его полностью понятным)
Для удаления элементов, мы также имеем 2 метода: remove
, pop
. Они противоположны симметричны add
, push
. Удалить, удаляет указанную модель из коллекции, pop удаляет последнюю модель в коллекции. Это показано в следующей спецификации,
describe('when removing items', function () { beforeEach(function () { collection = new FeedbackCollection(); }); beforeEach(function () { collection.push({id: 'feedback-1', email: '[email protected]', website: 'a.com', feedback: 'hello'}); collection.push({id: 'feedback-2', email: '[email protected]', website: 'b.com', feedback: 'good bye'}); }); describe('by remove method', function () { beforeEach(function () { var model = collection.get('feedback-1'); collection.remove(model); }); it('should be removed', function () { expect(collection.get('feedback-1')).not.toBeDefined(); }); }); describe('by pop method', function () { beforeEach(function () { collection.pop(); }); it('should be removed', function () { expect(collection.get('feedback-2')).not.toBeDefined(); }); }); });
Выводы
Мы только что ознакомились с базовыми функциями типа Backbone.Collection. В следующий раз мы подробнее рассмотрим коллекции, которые они генерируют, извлекают и сохраняют данные на сервере