Сгруппированная коллекция и представления с помощью Backbone.Marionette

У меня есть коллекция документов Backbone, которую я хочу сгруппировать по месяцам, чтобы все документы, созданные в течение одного месяца, были сгруппированы вместе. Я знаю, что я могу в основном добиться этого с помощью:

var byMonth = documents.groupBy(function(doc){
  return this.get('date').getMonth()
});

Теперь, когда у меня есть массив byMonth, как лучше всего настроить базовое представление, которое автоматически обновляется при добавлении элементов в коллекцию, изменении даты в одном из документов и т. д., чтобы документ автоматически перемещался в правильная группа и представления обновляются соответственно?

PS. Я также хочу показать совокупные данные о каждом месяце (например, количество документов и т. д.).

CompositeView от Marionette идеально подходит для таких вещей, но я не уверен, смогу ли я заставить его работать со сгруппированными коллекциями и как это сделать?


person ragulka    schedule 09.04.2013    source источник
comment
Я не думаю, что есть какой-либо собственный способ автоматического обновления вашей группы groupBy, поскольку на самом деле это не то, что используется Backbone (этот метод был добавлен скорее из любезности, я бы сказал).   -  person Loamhoof    schedule 09.04.2013
comment
Я тоже так думал. Я подумал, что могу использовать groupBy для создания небольших месячных коллекций и использовать для них CompositeView. Интересно, повторный запуск groupBy и сброс месячной коллекции из группы приведет к повторному отображению всего представления или просто обновлению того, что было изменено?   -  person ragulka    schedule 09.04.2013
comment
Я мало знаю о Marionette и о точной структуре вашего приложения, поэтому не могу сказать слишком много. Я думаю, что ваш вопрос слишком специфичен для конкретного случая.   -  person Loamhoof    schedule 09.04.2013


Ответы (2)


Я много думал об этом, и у меня нет идеального ответа, но вот мои заметки (мозговой дамп) на случай, если они помогут/подскажут вам новые идеи:

Лучший способ отобразить (и автоматически обновить) коллекцию, разбитую на 3 раздела/группы, например на этой неделе, на следующей неделе и в будущем

  1. Filter the collection outside the View: Create filtered sub-collections in the Controller, and pass one to each CollectionView. Then there's 2 options for modifying the collection:
    • A) directly modify the 3 sub-collections
      • Pro: auto-update will work, no filtering logic required in CollectionView, emptyView will work, it would be easy to have a count at the top of each section.
      • Минусы: нужно обойти эти 3 коллекции везде, где мы можем захотеть их изменить, и иметь эту дополнительную логику при добавлении/удалении события: if (event.time<next-week) {...} else if (event.time<upcoming) {...} else {...}. Можно абстрагировать все это в новый объект, который имеет ссылки на все 3 коллекции и имеет простые методы добавления/удаления, содержащие приведенную выше логику.
      • Еще один недостаток: элементы не могут перемещаться между этими подколлекциями, что может быть критическим, если группировки зависят от времени. Хотя я предполагаю, что в любом случае это произойдет только при полном повторном рендеринге, поэтому вы можете иметь метод updateSubCollections и вызывать его onBeforeRender или что-то еще.
    • B) only modify the parent collection - requires a listener on the main collection for manually re-filtering each sub-collection in the Controller on add/remove, and manually re-rendering all 3 list views.
      • Pro: CollectionView is real simple - no filtering logic etc, emptyView just works, it would be easy to have a count at the top of each section.
      • Против: вы теряете магию автоматического обновления (когда коллекция изменяется, ее collectionView автоматически обновляется) и каждый раз требуется тупой (дорогостоящий) повторный рендеринг всего collectionView.
  2. Filter the collection inside the View: Pass the full collection to each CollectionView, and override appendHTML method (or showCollection) to only display the ones that satisfy a condition.
    • Pro: get the auto-update magic, and still only dealing with a single collection everywhere in the code
    • Минусы: потребуется дополнительная логика, чтобы показать количество в верхней части каждого раздела. Требуется дополнительная логика, чтобы показать пустое представление или нет, И может иметь другие последствия, поскольку я сомневаюсь, что CollectionViews были разработаны для этого… они могут делать умные вещи со всеми элементами itemView в фоновом режиме — это может быть неэффективно — требует исследования. Просмотрите исходный код и найдите лучшую (самого высокого уровня) функцию, которую можно переопределить, чтобы не беспокоиться об обработке элементов, которые не имеют отношения к делу.
  3. Single view option: Filter the collection inside the view's appendHtml() function by manually appending each view in the right section of the page.
    • Pro: all the logic in one place, but probably not the right place (the CollectionView). Auto-update works. Still only dealing with a single collection everywhere in the code.
    • Против: потребуется дополнительная логика, чтобы показать количество в верхней части каждого раздела. Потребуется обрабатывать emptyView вручную для каждого раздела, что не так сложно — в вашем шаблоне просто определите все ваши разделы, содержащие пустые сообщения, а затем в вашей функции appendHtml, когда вы выясните, к какому разделу принадлежит элемент, просто скройте пустое сообщение для этого раздела, прежде чем добавлять его. Проблема заключается в том, как скрыть пустые разделы, когда элементы удаляются из коллекции. Единственный способ - прослушать событие удаления в коллекции и проверить все разделы, чтобы увидеть, пусты ли они? Или просто перерисовать все это.

Когда я впервые присоединился к своей нынешней команде, у них был старый код, реализующий вариант 1B, который я считал ужасным. Затем, когда мне пришлось реализовать что-то подобное, я выбрал вариант 3, который работал, но со временем стал немного запутанным. В следующий раз я думаю попробовать вариант 1А, но пока он не проверен...

Дайте мне знать, что вы в итоге сделали!

Редактировать: пара актуальных вопросов по проекту Marionette на Github:

person jackocnr    schedule 22.09.2014

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

Контроллер

App.Controller = function(){
  this.documents = new DocumentCollection();
  this.byMonthCollection = new ByMonthCollection(); 
  this.byMonthView = new ByMonthView({collection: this.byMonthCollection});
}

_.extend(App.Controller.prototype, {
  start: function() {
    this.documents.bind("change", _.bind(this.groupByMonth, this));
    this.documents.fetch();
  },
  groupByMonth: function() {
    var grouped = documents.groupBy(function(doc){
                    return this.get('date').getMonth() }); 

    this.byMonthCollection.reset(grouped);
  }
}
person Scott Puleo    schedule 09.04.2013