UNPKG

3.44 kBJavaScriptView Raw
1var _ = require('underscore');
2var BBEvents = require('backbone-events-standalone');
3var ampExtend = require('ampersand-class-extend');
4
5// options
6var options = ['collection', 'el', 'viewOptions', 'view', 'filter', 'reverse'];
7
8
9function CollectionView(spec) {
10 _.extend(this, _.pick(spec, options));
11 this.views = [];
12 this.listenTo(this.collection, 'add', this._addViewForModel);
13 this.listenTo(this.collection, 'remove', this._removeViewForModel);
14 this.listenTo(this.collection, 'move sort', this._renderAll);
15 this.listenTo(this.collection, 'refresh reset', this._reset);
16}
17
18_.extend(CollectionView.prototype, BBEvents, {
19 // for view contract compliance
20 render: function () {
21 this._renderAll();
22 return this;
23 },
24 remove: function () {
25 _.invoke(this.views, 'remove');
26 this.stopListening();
27 },
28 _getViewByModel: function (model) {
29 return _.find(this.views, function (view) {
30 return model === view.model;
31 });
32 },
33 _createViewForModel: function (model) {
34 var view = new this.view(_({model: model, collection: this.collection}).extend(this.viewOptions));
35 this.views.push(view);
36 view.parent = this;
37 view.renderedByParentView = true;
38 view.render();
39 },
40 _getOrCreateByModel: function (model) {
41 return this._getViewByModel(model) || this._createViewForModel(model);
42 },
43 _addViewForModel: function (model) {
44 var view = this._getViewByModel(model);
45 var matches = this.filter ? this.filter(model) : true;
46 if (!matches) {
47 return;
48 }
49 if (!view) {
50 view = new this.view(_({model: model, collection: this.collection}).extend(this.viewOptions));
51 this.views.push(view);
52 view.parent = this;
53 view.renderedByParentView = true;
54 view.render({containerEl: this.el});
55 }
56 this._insertView(view);
57 },
58 _insertView: function (view) {
59 if (!view.insertSelf) {
60 if (this.reverse) {
61 this.el.insertBefore(view.el, this.el.firstChild);
62 } else {
63 this.el.appendChild(view.el);
64 }
65 }
66 },
67 _removeViewForModel: function (model) {
68 var view = this._getViewByModel(model);
69 if (!view) {
70 return;
71 }
72 var index = this.views.indexOf(view);
73 if (index !== -1) {
74 // remove it if we found it calling animateRemove
75 // to give user option of gracefully destroying.
76 view = this.views.splice(index, 1)[0];
77 this._removeView(view);
78 }
79 },
80 _removeView: function (view) {
81 if (view.animateRemove) {
82 view.animateRemove();
83 } else {
84 view.remove();
85 }
86 },
87 _renderAll: function () {
88 this.collection.each(this._addViewForModel, this);
89 },
90 _reset: function () {
91 var newViews = [];
92 // get any existing views we may have cached
93 if (this.views.length) {
94 newViews = this.collection.map(this._getOrCreateByModel);
95 }
96 var toRemove = _.difference(this.views, newViews);
97 toRemove.forEach(this._removeView, this);
98 this.views = newViews;
99 this.views.forEach(this._insertView, this);
100 }
101});
102
103CollectionView.prototype.extend = ampExtend;
104
105module.exports = CollectionView;