forked from platanus/angular-restmod
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathcollection-api.js
More file actions
229 lines (210 loc) · 6.79 KB
/
Copy pathcollection-api.js
File metadata and controls
229 lines (210 loc) · 6.79 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
'use strict';
RMModule.factory('RMCollectionApi', ['RMUtils', function(Utils) {
var extend = angular.extend;
/**
* @class CollectionApi
*
* @extends ScopeApi
* @extends CommonApi
*
* @description
*
* A restmod collection is an extended array type bound REST resource route.
*
* Every time a new restmod model is created, an associated collection type is created too.
*
* TODO: talk about fetch/refresh behaviour, lifecycles, collection scopes, adding/removing
*
* For `$fetch` on a collection:
*
* * before-fetch-many
* * before-request
* * after-request[-error]
* * after-feed (only called if no errors)
* * after-fetch-many[-error]
*
* @property {boolean} $isCollection Helper flag to separate collections from the main type
* @property {object} $scope The collection scope (hierarchical scope, not angular scope)
* @property {object} $params The collection query parameters
* @property {boolean} $resolved The collection resolve status
*
*/
return {
/**
* @memberof CollectionApi#
*
* @description Gets this collection url without query string.
*
* @return {string} The collection url.
*/
$url: function() {
return this.$scope.$url();
},
/**
* @memberof CollectionApi#
*
* @description Part of the scope interface, provides urls for collection's items.
*
* @param {RecordApi} _pk Item key to provide the url to.
* @return {string|null} The url or nill if item does not meet the url requirements.
*/
$urlFor: function(_pk) {
// force item unscoping if model is not anonymous (maybe make this optional)
var baseUrl = this.$type.$url();
return Utils.joinUrl(baseUrl ? baseUrl : this.$url(), _pk);
},
/**
* @memberof CollectionApi#
*
* @description Feeds raw collection data into the collection, marks collection as $resolved
*
* This method is for use in collections only.
*
* @param {array} _raw Data to add
* @return {CollectionApi} self
*/
$feed: function(_raw) {
if(!_raw || !angular.isArray(_raw)) {
throw new Error('Error in resource {0} configuration. Expected response to be array');
}
if(!this.$resolved) this.length = 0; // reset contents if not resolved.
for(var i = 0, l = _raw.length; i < l; i++) {
this.$buildRaw(_raw[i]).$reveal(); // build and disclose every item.
}
this.$resolved = true;
return this;
},
/**
* @memberof CollectionApi#
*
* @description
*
* Unpacks and decode raw data from a server generated structure into this collection.
*
* ATTENTION: do not override this method to change the object wrapping strategy,
* instead, check {@link BuilderApi#setPacker} for instruction about loading a new packer.
*
* @param {mixed} _raw Raw server data
* @return {CollectionApi} this
*/
$unwrap: function(_raw) {
_raw = this.$$unpack(_raw);
return this.$feed(_raw);
},
/**
* @memberof CollectionApi#
*
* @description Resets the collection's resolve status.
*
* This method is for use in collections only.
*
* @return {CollectionApi} self
*/
$reset: function() {
this.$cancel(); // cancel pending requests.
this.$resolved = false;
return this;
},
/**
* @memberof CollectionApi#
*
* @description Begin a server request to populate collection. This method does not
* clear the collection contents, use `$refresh` to reset and fetch.
*
* This method is for use in collections only.
*
* @param {object|function} _params Additional request parameters, not stored in collection,
* if a function is given, then it will be called with the request object to allow requet customization.
* @return {CollectionApi} self
*/
$fetch: function(_params) {
var request = { method: 'GET', url: this.$url(), params: this.$params };
if(_params) {
request.params = request.params ? extend(request.params, _params) : _params;
}
// TODO: check that collection is bound.
this.$dispatch('before-fetch-many', [request]);
this.$send(request, function(_response) {
this.$unwrap(_response.data); // feed retrieved data.
this.$dispatch('after-fetch-many', [_response]);
}, function(_response) {
this.$dispatch('after-fetch-many-error', [_response]);
});
return this;
},
/**
* @memberof CollectionApi#
*
* @description Resets and fetches content.
*
* @param {object} _params `$fetch` params
* @return {CollectionApi} self
*/
$refresh: function(_params) {
return this.$reset().$fetch(_params);
},
/**
* @memberof CollectionApi#
*
* @description Adds an item to the back of the collection. This method does not attempt to send changes
* to the server. To create a new item and add it use $create or $build.
*
* Triggers after-add callbacks.
*
* @param {RecordApi} _obj Item to be added
* @return {CollectionApi} self
*/
$add: function(_obj, _idx) {
// TODO: make sure object is f type Model?
if(_obj.$position === undefined) {
if(_idx !== undefined) {
this.splice(_idx, 0, _obj);
} else {
this.push(_obj);
}
_obj.$position = true; // use true for now, keeping position updated can be expensive
this.$dispatch('after-add', [_obj]);
}
return this;
},
/**
* @memberof CollectionApi#
*
* @description Removes an item from the collection.
*
* This method does not send a DELETE request to the server, it just removes the
* item locally. To remove an item AND send a DELETE use the item's $destroy method.
*
* Triggers after-remove callbacks.
*
* @param {RecordApi} _obj Item to be removed
* @return {CollectionApi} self
*/
$remove: function(_obj) {
var idx = this.$indexOf(_obj);
if(idx !== -1) {
this.splice(idx, 1);
_obj.$position = undefined;
this.$dispatch('after-remove', [_obj]);
}
return this;
},
/**
* @memberof CollectionApi#
*
* @description Finds the location of an object in the array.
*
* If a function is provided then the index of the first item for which the function returns true is returned.
*
* @param {RecordApi|function} _obj Object to find
* @return {number} Object index or -1 if not found
*/
$indexOf: function(_obj) {
var accept = typeof _obj === 'function' ? _obj : false;
for(var i = 0, l = this.length; i < l; i++) {
if(accept ? accept(this[i]) : this[i] === _obj) return i;
}
return -1;
}
};
}]);