Заметки с тегом «backbone»

Операции над коллекциями объектов Backbone.js

Для работы с коллекциями в Backbone.js импортируется 28 методов из библиотеки Underscore.js. Среди них, пожалуй, самыми востребованными являются .forEach(), .filter(), .reject(), .find(), .without().

Предположим, что нужно найти в коллекции элементы, соответствующие определённому условию и уничтожить их.

var boxes = new Boxes(); // коллекция объектов
// предположим, что она уже заполнена нужными данными
var boxesToDestroy = boxes.filter(function (box) {
  return box.isReadyToDestroy();
});
_.forEach(boxesToDestroy, function (box) {
  box.destroy();
});

Выглядит громоздко. Всё это по тому, что методы Underscore.js возвращают массив, а не новый экземпляр коллекции.

Благо в Underscore.js есть метод .chain(), который позволяет всё значительно упростить.

boxes.chain()
  .filter(function (box) { return box.isReadyToDestroy(); })
  .forEach(function (box) { box.destroy(); });

Этот код можно ещё упростить, воспользовавшись методом .invoke().

boxes.chain()
  .filter(function (box) { return box.isReadyToDestroy(); })
  .invoke("destroy");

Так можно стоить цепочки из любых операций над элементами коллекции. Если по завершению цепочки нужно будет сохранить результат выборки в переменную, то последним вызовем метод .value().

Оставте свой комментарий

Встреча FrontTalks 20 апреля 2013 года

На сайте FrontTalks появились видео докладов со второй встречи фронт-энд разработчиков.

Я там выступал с рассказом о полиморфизме в Backbone.js приложении. Слайды презентации на Гитхабе. Описание того, как работает фабрика фабрик, можно прочитать в статье «Коллекция разнотипных объектов в Backbone.js».

Другие примеры, которые я готовил для этой встречи, можно посмотреть в репозитоии — https://github.com/mistakster/fronttalks-bb

O мероприятии:

Вторая встреча FrontTalks
Докладчики: Владимир Кузнецов, Алексей Охрименко, Олег Мохов, Михаил Давыдов
Участие бесплатно
Комментарии к заметке: 3

Плагин Backbone.Native

У Backbone среди зависимостей есть жесткая привязка jQuery. С помощью неё обеспечивается манипуляция с DOM-элементами, XHR-запросы и делегация событий. Всё это было реализовано в компактном плагине Backbone.Native.

Комментарии к заметке: 3

Кеширование медленных вычислений

Когда функция неоднократно вызывается с одними и теми же параметрами, и каждый раз возвращает для этих параметров одинаковый результат, то можно сохранить его и сразу возвращать без долгих вычислений.

В библиотеке Underscore.js есть метод, который для любой функции делает версию этой функции с кешированием — memoize.

Так, например, её можно применять для кеширования _.template().

var View = Backbone.View.extend({
  render: function () {
    var markup = _.template(this.template, this.model.toJSON());
    this.$el.html(markup);
  }
});

Вместо того, чтобы при каждом обновлении данных компилировать тектовый шаблон, он будет преобразован в функцию только при первом использовании. А все последующие вызовы будут использовать уже скомпилированный вариант.

var cachedTemplate = _.memoize(_template);
var View = Backbone.View.extend({
  render: function () {
    var markup = cachedTemplate(this.template, this.model.toJSON());
    this.$el.html(markup);
  }
});

Разница в производительности при рендеринге простенького шаблона и его кешированного варианта налицо.

Все скомпилированные шаблоны находятся в хранилище ключ-значение внутри замыкания _.memoize(). Ключом в нашем случае выступает текст шаблона, а значением — скомпилированная функция.

В качестве ключа может быть не только текстовая строка. Если первый параметр кешируемой функции не является строкой, то ключом станет то, что венёт его метод .toString(). В некоторых случаях это может оказаться неприемлемо (например, когда первым аргументом идёт объект). Чтобы избежать коллизий, в _.memoize() можно передать опциональную функцию, генерирующую ключ из аргументов.

var cachedTemplate = _.memoize(_.template, function (value) {
  return JSON.stringify(value);
});

Аналогичный приём, но без использования Underscore.js я применяю для кеширования асинхронных запросов.

Комментарии к заметке: 3

Коллекция разнотипных объектов в Backbone.js

В коллекциях Backbone.js не обязательно должны храниться объекты одного типа. Если в неё явно добавить экземпляр класса, унаследованного от Backbone.Model, то он без изменений будет помещён в неё. У коллекции есть атрибут model, в котором хранится конструктор модели. Когда в коллекцию добавляется простой объект, то он в качестве параметра передаётся в этот конструктор.

В атрибуте model можно, фактически, указать любую функцию. Возвращаемые значения (экземпляры классов, унаследованных от Backbone.Model) будут помещаться в коллекцию. Такие функции ещё называют фабриками или фабричными методами. В документации к атрибуту model как раз представлен вариант такой фабрики.

Чтобы каждый раз не писать однотипных проверок в подобных ситуациях, я предлагаю пользоваться паттерном «Фабрика фабрик»:

App.Factory = function (getter, hash, def) {
  return function () {
    var value = getter.apply(this, arguments);
    var ctor = hash[value] ? hash[value] : def;
    return new ctor(arguments[0], arguments[1]);
  };
};
  • getter — функция, которая возвращает значение, на основании которого будет выбираться требуемый конструктор;
  • hash — объект ключ-значение с перечислением конструкторов, которые могут использоваться фабрикой;
  • def — конструктор по-умолчанию.

Предположим, что в коллекции могут находиться объекты следующих классов:

App.Item = Backbone.Model.extend({});
App.ItemNews = App.Item.extend({});
App.ItemAction = App.Item.extend({});
App.ItemMessage = App.Item.extend({});

Таким образом, фабрика объектов будет выглядеть так:

App.ItemFactory = App.Factory(
  function (attr) { return attr.type; },
  {
    "news": App.ItemNews,
    "action": App.ItemAction,
    "message": App.ItemMessage
  },
  App.Item
);

App.Factory возвращает функцию, которая, в свою очередь, будет возвращать требуемый экземпляр в зависимости от значения атрибута type. Эту функцию и передаём в описание коллекции.

App.Items = Backbone.Collection.extend({
  model: App.ItemFactory
});

Теперь коллекция будет заполняться моделями нужного типа автоматически.

Практическое применение паттерна можно посмотреть в исходном коде примера.

Комментарии к заметке: 15