@converse/skeletor 0.0.9 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +5 -89
- package/dist/skeletor.d.ts +764 -0
- package/dist/skeletor.js +7568 -7747
- package/dist/skeletor.min.js +6 -11
- package/package.json +46 -30
- package/src/{collection.js → collection.ts} +232 -271
- package/src/drivers/{sessionStorage.js → sessionStorage.ts} +81 -93
- package/src/{eventemitter.js → eventemitter.ts} +44 -59
- package/src/{helpers.js → helpers.ts} +56 -69
- package/src/index.ts +84 -0
- package/src/{listening.js → listening.ts} +32 -27
- package/src/{model.js → model.ts} +143 -162
- package/src/{storage.js → storage.ts} +92 -49
- package/src/types.ts +189 -0
- package/src/utils/{events.js → events.ts} +98 -48
- package/dist/skeletor.js.map +0 -1
- package/dist/skeletor.min.js.map +0 -1
- package/src/element.js +0 -238
- package/src/index.js +0 -47
- package/src/types/collection.d.ts +0 -366
- package/src/types/collection.d.ts.map +0 -1
- package/src/types/drivers/sessionStorage.d.ts +0 -37
- package/src/types/drivers/sessionStorage.d.ts.map +0 -1
- package/src/types/element.d.ts +0 -115
- package/src/types/element.d.ts.map +0 -1
- package/src/types/eventemitter.d.ts +0 -93
- package/src/types/eventemitter.d.ts.map +0 -1
- package/src/types/helpers.d.ts +0 -62
- package/src/types/helpers.d.ts.map +0 -1
- package/src/types/index.d.ts +0 -19
- package/src/types/index.d.ts.map +0 -1
- package/src/types/listening.d.ts +0 -40
- package/src/types/listening.d.ts.map +0 -1
- package/src/types/model.d.ts +0 -230
- package/src/types/model.d.ts.map +0 -1
- package/src/types/storage.d.ts +0 -35
- package/src/types/storage.d.ts.map +0 -1
- package/src/types/utils/events.d.ts +0 -28
- package/src/types/utils/events.d.ts.map +0 -1
|
@@ -1,81 +1,76 @@
|
|
|
1
|
-
import { getResolveablePromise, getSyncMethod, urlError, wrapError } from './helpers
|
|
2
|
-
import clone from 'lodash-es/clone
|
|
3
|
-
import defaults from 'lodash-es/defaults
|
|
4
|
-
import defer from 'lodash-es/defer
|
|
5
|
-
import has from 'lodash-es/has
|
|
6
|
-
import invert from 'lodash-es/invert
|
|
7
|
-
import isEmpty from 'lodash-es/isEmpty
|
|
8
|
-
import isEqual from 'lodash-es/isEqual
|
|
9
|
-
import iteratee from 'lodash-es/iteratee
|
|
10
|
-
import omit from 'lodash-es/omit
|
|
11
|
-
import pick from 'lodash-es/pick
|
|
12
|
-
import result from 'lodash-es/result
|
|
13
|
-
import uniqueId from 'lodash-es/uniqueId
|
|
14
|
-
import
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
* @typedef {Record.<string, any>} Options
|
|
21
|
-
* @property {boolean} [validate]
|
|
22
|
-
*
|
|
23
|
-
* @typedef {Record.<string, any>} ModelOptions
|
|
24
|
-
* @property {Collection} [collection]
|
|
25
|
-
* @property {boolean} [parse]
|
|
26
|
-
* @property {boolean} [unset]
|
|
27
|
-
* @property {boolean} [silent]
|
|
28
|
-
*/
|
|
1
|
+
import { getResolveablePromise, getSyncMethod, urlError, wrapError } from './helpers';
|
|
2
|
+
import clone from 'lodash-es/clone';
|
|
3
|
+
import defaults from 'lodash-es/defaults';
|
|
4
|
+
import defer from 'lodash-es/defer';
|
|
5
|
+
import has from 'lodash-es/has';
|
|
6
|
+
import invert from 'lodash-es/invert';
|
|
7
|
+
import isEmpty from 'lodash-es/isEmpty';
|
|
8
|
+
import isEqual from 'lodash-es/isEqual';
|
|
9
|
+
import iteratee from 'lodash-es/iteratee';
|
|
10
|
+
import omit from 'lodash-es/omit';
|
|
11
|
+
import pick from 'lodash-es/pick';
|
|
12
|
+
import result from 'lodash-es/result';
|
|
13
|
+
import uniqueId from 'lodash-es/uniqueId';
|
|
14
|
+
import { EventEmitterObject } from './eventemitter';
|
|
15
|
+
|
|
16
|
+
// Import types
|
|
17
|
+
import type { Collection } from './collection';
|
|
18
|
+
import type Storage from './storage';
|
|
19
|
+
import { ModelAttributes, ObjectWithId, SyncOperation, ModelOptions, Options } from './types';
|
|
29
20
|
|
|
30
21
|
/**
|
|
22
|
+
* @public
|
|
31
23
|
* **Models** are the basic data object in the framework --
|
|
32
24
|
* frequently representing a row in a table in a database on your server.
|
|
33
25
|
* A discrete chunk of data and a bunch of useful, related methods for
|
|
34
26
|
* performing computations and transformations on that data.
|
|
35
27
|
*/
|
|
36
|
-
class Model extends
|
|
28
|
+
export class Model<T extends ModelAttributes = ModelAttributes> extends EventEmitterObject {
|
|
29
|
+
_browserStorage?: Storage;
|
|
30
|
+
_changing = false;
|
|
31
|
+
_pending: boolean | ModelOptions = false;
|
|
32
|
+
_previousAttributes?: T;
|
|
33
|
+
_url: string = '';
|
|
34
|
+
_urlRoot: string;
|
|
35
|
+
attributes: T;
|
|
36
|
+
changed: Partial<T> = {};
|
|
37
|
+
cid: string;
|
|
38
|
+
collection?: Collection;
|
|
39
|
+
id: string | number;
|
|
40
|
+
validationError: string | number | null = null;
|
|
41
|
+
|
|
37
42
|
/**
|
|
38
43
|
* Create a new model with the specified attributes. A client id (`cid`)
|
|
39
44
|
* is automatically generated and assigned for you.
|
|
40
|
-
* @param {Attributes} [attributes]
|
|
41
|
-
* @param {ModelOptions} [options]
|
|
42
45
|
*/
|
|
43
|
-
constructor(attributes
|
|
46
|
+
constructor(attributes?: Partial<T>, options?: ModelOptions) {
|
|
44
47
|
super();
|
|
45
|
-
let attrs = attributes || {};
|
|
46
|
-
options
|
|
47
|
-
this.preinitialize.apply(this, arguments);
|
|
48
|
-
this.cid = uniqueId(this.cidPrefix);
|
|
49
|
-
this.attributes = {};
|
|
48
|
+
let attrs: Partial<T> = attributes || ({} as Partial<T>);
|
|
49
|
+
options = options || {};
|
|
50
50
|
|
|
51
|
-
|
|
52
|
-
this.
|
|
51
|
+
this.preinitialize.apply(this, arguments as any);
|
|
52
|
+
this.cid = uniqueId(this.cidPrefix);
|
|
53
|
+
this.attributes = {} as T;
|
|
53
54
|
|
|
54
55
|
if (options.collection) this.collection = options.collection;
|
|
55
|
-
if (options.parse) attrs = this.parse(attrs, options) || {};
|
|
56
|
+
if (options.parse) attrs = this.parse(attrs, options) || ({} as Partial<T>);
|
|
56
57
|
|
|
57
58
|
const default_attrs = result(this, 'defaults');
|
|
58
59
|
attrs = defaults(Object.assign({}, default_attrs, attrs), default_attrs);
|
|
59
60
|
|
|
60
61
|
this.set(attrs, options);
|
|
61
62
|
|
|
62
|
-
this.initialize.apply(this, arguments);
|
|
63
|
+
this.initialize.apply(this, arguments as any);
|
|
63
64
|
|
|
64
|
-
//
|
|
65
|
+
// Reset changed after initial set
|
|
65
66
|
this.changed = {};
|
|
66
67
|
}
|
|
67
68
|
|
|
68
|
-
|
|
69
|
-
* @param {Storage} storage
|
|
70
|
-
*/
|
|
71
|
-
set browserStorage(storage) {
|
|
69
|
+
set browserStorage(storage: Storage) {
|
|
72
70
|
this._browserStorage = storage;
|
|
73
71
|
}
|
|
74
72
|
|
|
75
|
-
|
|
76
|
-
* @returns {Storage} storage
|
|
77
|
-
*/
|
|
78
|
-
get browserStorage() {
|
|
73
|
+
get browserStorage(): Storage | undefined {
|
|
79
74
|
return this._browserStorage;
|
|
80
75
|
}
|
|
81
76
|
|
|
@@ -84,8 +79,7 @@ class Model extends EventEmitter(Object) {
|
|
|
84
79
|
* CouchDB users may want to set this to `"_id"` (by overriding this getter
|
|
85
80
|
* in a subclass).
|
|
86
81
|
*/
|
|
87
|
-
|
|
88
|
-
get idAttribute() {
|
|
82
|
+
get idAttribute(): string {
|
|
89
83
|
return 'id';
|
|
90
84
|
}
|
|
91
85
|
|
|
@@ -93,8 +87,7 @@ class Model extends EventEmitter(Object) {
|
|
|
93
87
|
* The prefix is used to create the client id which is used to identify models locally.
|
|
94
88
|
* You may want to override this if you're experiencing name clashes with model ids.
|
|
95
89
|
*/
|
|
96
|
-
|
|
97
|
-
get cidPrefix() {
|
|
90
|
+
get cidPrefix(): string {
|
|
98
91
|
return 'c';
|
|
99
92
|
}
|
|
100
93
|
|
|
@@ -102,131 +95,121 @@ class Model extends EventEmitter(Object) {
|
|
|
102
95
|
* preinitialize is an empty function by default. You can override it with a function
|
|
103
96
|
* or object. preinitialize will run before any instantiation logic is run in the Model.
|
|
104
97
|
*/
|
|
105
|
-
|
|
106
|
-
preinitialize() {}
|
|
98
|
+
preinitialize(...args: any[]): void {}
|
|
107
99
|
|
|
108
100
|
/**
|
|
109
101
|
* Initialize is an empty function by default. Override it with your own
|
|
110
102
|
* initialization logic.
|
|
111
|
-
* @param {Attributes} [attrs]
|
|
112
|
-
* @param {ModelOptions} [options]
|
|
113
103
|
*/
|
|
114
|
-
initialize(attrs
|
|
104
|
+
initialize(attrs?: Partial<T>, options?: ModelOptions): void {}
|
|
105
|
+
|
|
106
|
+
validate(attrs: Partial<T> | ObjectWithId, options?: ModelOptions): string | number | null | void {
|
|
107
|
+
return null;
|
|
108
|
+
}
|
|
115
109
|
|
|
116
110
|
/**
|
|
117
|
-
*
|
|
118
|
-
* @param {object} [options]
|
|
119
|
-
* @returns {string} The validation error message
|
|
111
|
+
* Return a hash of defaults for the model's attributes.
|
|
120
112
|
*/
|
|
121
|
-
|
|
122
|
-
return
|
|
113
|
+
defaults(): Partial<T> {
|
|
114
|
+
return {} as Partial<T>;
|
|
123
115
|
}
|
|
124
116
|
|
|
125
117
|
/**
|
|
126
118
|
* Return a copy of the model's `attributes` object.
|
|
127
119
|
*/
|
|
128
|
-
toJSON() {
|
|
120
|
+
toJSON(): T {
|
|
129
121
|
return clone(this.attributes);
|
|
130
122
|
}
|
|
131
123
|
|
|
132
124
|
/**
|
|
133
125
|
* Override this if you need custom syncing semantics for *this* particular model.
|
|
134
|
-
* @param {'create'|'update'|'patch'|'delete'|'read'} method
|
|
135
|
-
* @param {Model} model
|
|
136
|
-
* @param {Options} options
|
|
137
126
|
*/
|
|
138
|
-
|
|
139
|
-
sync(method, model, options) {
|
|
127
|
+
sync(method: SyncOperation, model: Model<any>, options: Options): any {
|
|
140
128
|
return getSyncMethod(model)(method, model, options);
|
|
141
129
|
}
|
|
142
130
|
|
|
143
131
|
/**
|
|
144
132
|
* Get the value of an attribute.
|
|
145
|
-
* @param {string} attr
|
|
146
133
|
*/
|
|
147
|
-
get(attr) {
|
|
134
|
+
get<K extends keyof T>(attr: K): T[K] {
|
|
148
135
|
return this.attributes[attr];
|
|
149
136
|
}
|
|
150
137
|
|
|
151
|
-
keys() {
|
|
138
|
+
keys(): string[] {
|
|
152
139
|
return Object.keys(this.attributes);
|
|
153
140
|
}
|
|
154
141
|
|
|
155
|
-
values() {
|
|
142
|
+
values(): T[keyof T][] {
|
|
156
143
|
return Object.values(this.attributes);
|
|
157
144
|
}
|
|
158
145
|
|
|
159
|
-
pairs() {
|
|
146
|
+
pairs(): [keyof T, T[keyof T]][] {
|
|
160
147
|
return this.entries();
|
|
161
148
|
}
|
|
162
149
|
|
|
163
|
-
entries() {
|
|
164
|
-
return Object.entries(this.attributes);
|
|
150
|
+
entries(): [keyof T, T[keyof T]][] {
|
|
151
|
+
return Object.entries(this.attributes) as [keyof T, T[keyof T]][];
|
|
165
152
|
}
|
|
166
153
|
|
|
167
|
-
invert() {
|
|
154
|
+
invert(): Record<string, keyof T> {
|
|
168
155
|
return invert(this.attributes);
|
|
169
156
|
}
|
|
170
157
|
|
|
171
|
-
pick(...args) {
|
|
158
|
+
pick<K extends keyof T>(...args: K[]): Pick<T, K> {
|
|
172
159
|
if (args.length === 1 && Array.isArray(args[0])) {
|
|
173
|
-
args = args[0];
|
|
160
|
+
args = args[0] as K[];
|
|
174
161
|
}
|
|
175
|
-
return pick(this.attributes, args)
|
|
162
|
+
return pick(this.attributes, args) as Pick<T, K>;
|
|
176
163
|
}
|
|
177
164
|
|
|
178
|
-
omit(...args) {
|
|
165
|
+
omit<K extends keyof T>(...args: K[]): Omit<T, K> {
|
|
179
166
|
if (args.length === 1 && Array.isArray(args[0])) {
|
|
180
|
-
args = args[0];
|
|
167
|
+
args = args[0] as K[];
|
|
181
168
|
}
|
|
182
|
-
return omit(this.attributes, args)
|
|
169
|
+
return omit(this.attributes, args) as Omit<T, K>;
|
|
183
170
|
}
|
|
184
171
|
|
|
185
|
-
isEmpty() {
|
|
172
|
+
isEmpty(): boolean {
|
|
186
173
|
return isEmpty(this.attributes);
|
|
187
174
|
}
|
|
188
175
|
|
|
189
176
|
/**
|
|
190
177
|
* Returns `true` if the attribute contains a value that is not null
|
|
191
178
|
* or undefined.
|
|
192
|
-
* @param {string} attr
|
|
193
179
|
*/
|
|
194
|
-
has(attr) {
|
|
180
|
+
has(attr: keyof T): boolean {
|
|
195
181
|
return this.get(attr) != null;
|
|
196
182
|
}
|
|
197
183
|
|
|
198
184
|
/**
|
|
199
185
|
* Special-cased proxy to lodash's `matches` method.
|
|
200
|
-
* @param {Attributes} attrs
|
|
201
186
|
*/
|
|
202
|
-
matches(attrs) {
|
|
203
|
-
return !!iteratee(attrs
|
|
187
|
+
matches(attrs: Partial<T>): boolean {
|
|
188
|
+
return !!iteratee(attrs)(this.attributes);
|
|
204
189
|
}
|
|
205
190
|
|
|
206
191
|
/**
|
|
207
192
|
* Set a hash of model attributes on the object, firing `"change"`. This is
|
|
208
193
|
* the core primitive operation of a model, updating the data and notifying
|
|
209
194
|
* anyone who needs to know about the change in state. The heart of the beast.
|
|
210
|
-
* @param {string|Object} key
|
|
211
|
-
* @param {string|Object} [val]
|
|
212
|
-
* @param {Options} [options]
|
|
213
195
|
*/
|
|
214
|
-
set(key, val, options) {
|
|
196
|
+
set(key: string | Partial<T> | ObjectWithId, val?: any, options?: ModelOptions): this {
|
|
215
197
|
if (key == null) return this;
|
|
216
198
|
|
|
217
199
|
// Handle both `"key", value` and `{key: value}` -style arguments.
|
|
218
|
-
let attrs;
|
|
200
|
+
let attrs: Partial<T> | ObjectWithId;
|
|
219
201
|
if (typeof key === 'object') {
|
|
220
202
|
attrs = key;
|
|
221
203
|
options = val;
|
|
222
204
|
} else {
|
|
223
|
-
|
|
205
|
+
attrs = {} as Partial<T>;
|
|
206
|
+
attrs[key as keyof T] = val;
|
|
224
207
|
}
|
|
225
208
|
|
|
226
|
-
options
|
|
209
|
+
options = options || {};
|
|
227
210
|
|
|
228
211
|
// Run validation.
|
|
229
|
-
if (!this._validate(attrs, options)) return
|
|
212
|
+
if (!this._validate(attrs, options)) return null;
|
|
230
213
|
|
|
231
214
|
// Extract attributes and options.
|
|
232
215
|
const unset = options.unset;
|
|
@@ -246,14 +229,14 @@ class Model extends EventEmitter(Object) {
|
|
|
246
229
|
|
|
247
230
|
// For each `set` attribute, update or delete the current value.
|
|
248
231
|
for (const attr in attrs) {
|
|
249
|
-
val = attrs[attr];
|
|
250
|
-
if (!isEqual(current[attr], val)) changes.push(attr);
|
|
251
|
-
if (!isEqual(prev[attr], val)) {
|
|
252
|
-
changed[attr] = val;
|
|
232
|
+
val = attrs[attr as keyof (T | ObjectWithId)];
|
|
233
|
+
if (!isEqual(current[attr as keyof T], val)) changes.push(attr);
|
|
234
|
+
if (!isEqual(prev[attr as keyof T], val)) {
|
|
235
|
+
changed[attr as keyof T] = val;
|
|
253
236
|
} else {
|
|
254
|
-
delete changed[attr];
|
|
237
|
+
delete changed[attr as keyof T];
|
|
255
238
|
}
|
|
256
|
-
unset ? delete current[attr] : (current[attr] = val);
|
|
239
|
+
unset ? delete current[attr as keyof T] : (current[attr as keyof T] = val);
|
|
257
240
|
}
|
|
258
241
|
|
|
259
242
|
// Update the `id`.
|
|
@@ -267,12 +250,13 @@ class Model extends EventEmitter(Object) {
|
|
|
267
250
|
}
|
|
268
251
|
}
|
|
269
252
|
|
|
270
|
-
// You might be wondering why there's a `while` loop here. Changes can
|
|
271
|
-
// be recursively nested within `"change"` events.
|
|
272
253
|
if (changing) return this;
|
|
254
|
+
|
|
273
255
|
if (!silent) {
|
|
256
|
+
// You might be wondering why there's a `while` loop here. Changes can
|
|
257
|
+
// be recursively nested within `"change"` events.
|
|
274
258
|
while (this._pending) {
|
|
275
|
-
options =
|
|
259
|
+
options = this._pending as ModelOptions;
|
|
276
260
|
this._pending = false;
|
|
277
261
|
this.trigger('change', this, options);
|
|
278
262
|
}
|
|
@@ -285,31 +269,27 @@ class Model extends EventEmitter(Object) {
|
|
|
285
269
|
/**
|
|
286
270
|
* Remove an attribute from the model, firing `"change"`. `unset` is a noop
|
|
287
271
|
* if the attribute doesn't exist.
|
|
288
|
-
* @param {string} attr
|
|
289
|
-
* @param {Options} [options]
|
|
290
272
|
*/
|
|
291
|
-
unset(attr, options) {
|
|
292
|
-
return this.set(attr, undefined, Object.assign({}, options, { unset: true }));
|
|
273
|
+
unset(attr: keyof T, options?: ModelOptions): this {
|
|
274
|
+
return this.set(attr as string, undefined, Object.assign({}, options, { unset: true }));
|
|
293
275
|
}
|
|
294
276
|
|
|
295
277
|
/**
|
|
296
278
|
* Clear all attributes on the model, firing `"change"`.
|
|
297
|
-
* @param {Options} options
|
|
298
279
|
*/
|
|
299
|
-
clear(options) {
|
|
300
|
-
const attrs = {};
|
|
301
|
-
for (const key in this.attributes) attrs[key] = undefined;
|
|
280
|
+
clear(options?: ModelOptions): this {
|
|
281
|
+
const attrs: Partial<T> = {};
|
|
282
|
+
for (const key in this.attributes) attrs[key as keyof T] = undefined;
|
|
302
283
|
return this.set(attrs, Object.assign({}, options, { unset: true }));
|
|
303
284
|
}
|
|
304
285
|
|
|
305
286
|
/**
|
|
306
287
|
* Determine if the model has changed since the last `"change"` event.
|
|
307
288
|
* If you specify an attribute name, determine if that attribute has changed.
|
|
308
|
-
* @param {string} [attr]
|
|
309
289
|
*/
|
|
310
|
-
hasChanged(attr) {
|
|
290
|
+
hasChanged(attr?: keyof T): boolean {
|
|
311
291
|
if (attr == null) return !isEmpty(this.changed);
|
|
312
|
-
return has(this.changed, attr);
|
|
292
|
+
return has(this.changed, attr as string);
|
|
313
293
|
}
|
|
314
294
|
|
|
315
295
|
/**
|
|
@@ -319,20 +299,19 @@ class Model extends EventEmitter(Object) {
|
|
|
319
299
|
* persisted to the server. Unset attributes will be set to undefined.
|
|
320
300
|
* You can also pass an attributes object to diff against the model,
|
|
321
301
|
* determining if there *would be* a change.
|
|
322
|
-
* @param {Object} diff
|
|
323
302
|
*/
|
|
324
|
-
changedAttributes(diff) {
|
|
303
|
+
changedAttributes(diff?: Partial<T>): Partial<T> | false {
|
|
325
304
|
if (!diff) {
|
|
326
305
|
return this.hasChanged() ? clone(this.changed) : false;
|
|
327
306
|
}
|
|
328
307
|
|
|
329
308
|
const old = this._changing ? this._previousAttributes : this.attributes;
|
|
330
|
-
const changed = {};
|
|
331
|
-
let hasChanged;
|
|
309
|
+
const changed: Partial<T> = {};
|
|
310
|
+
let hasChanged = false;
|
|
332
311
|
for (const attr in diff) {
|
|
333
|
-
const val = diff[attr];
|
|
334
|
-
if (isEqual(old[attr], val)) continue;
|
|
335
|
-
changed[attr] = val;
|
|
312
|
+
const val = diff[attr as keyof T];
|
|
313
|
+
if (isEqual(old[attr as keyof T], val)) continue;
|
|
314
|
+
changed[attr as keyof T] = val;
|
|
336
315
|
hasChanged = true;
|
|
337
316
|
}
|
|
338
317
|
return hasChanged ? changed : false;
|
|
@@ -341,9 +320,8 @@ class Model extends EventEmitter(Object) {
|
|
|
341
320
|
/**
|
|
342
321
|
* Get the previous value of an attribute, recorded at the time the last
|
|
343
322
|
* `"change"` event was fired.
|
|
344
|
-
* @param {string} [attr]
|
|
345
323
|
*/
|
|
346
|
-
previous(attr) {
|
|
324
|
+
previous<K extends keyof T>(attr: K): T[K] | null {
|
|
347
325
|
if (attr == null || !this._previousAttributes) return null;
|
|
348
326
|
return this._previousAttributes[attr];
|
|
349
327
|
}
|
|
@@ -352,21 +330,20 @@ class Model extends EventEmitter(Object) {
|
|
|
352
330
|
* Get all of the attributes of the model at the time of the previous
|
|
353
331
|
* `"change"` event.
|
|
354
332
|
*/
|
|
355
|
-
previousAttributes() {
|
|
356
|
-
return clone(this._previousAttributes);
|
|
333
|
+
previousAttributes(): T | undefined {
|
|
334
|
+
return this._previousAttributes ? clone(this._previousAttributes) : undefined;
|
|
357
335
|
}
|
|
358
336
|
|
|
359
337
|
/**
|
|
360
338
|
* Fetch the model from the server, merging the response with the model's
|
|
361
339
|
* local attributes. Any changed attributes will trigger a "change" event.
|
|
362
|
-
* @param {Options} [options={}]
|
|
363
340
|
*/
|
|
364
|
-
fetch(options={}) {
|
|
341
|
+
fetch(options: Options = {}): any {
|
|
365
342
|
options = Object.assign({ parse: true }, options);
|
|
366
343
|
|
|
367
344
|
const success = options.success;
|
|
368
345
|
|
|
369
|
-
options.success = (resp) => {
|
|
346
|
+
options.success = (resp: any) => {
|
|
370
347
|
const serverAttrs = options.parse ? this.parse(resp, options) : resp;
|
|
371
348
|
if (!this.set(serverAttrs, options)) return false;
|
|
372
349
|
if (success) success.call(options.context, this, resp, options);
|
|
@@ -381,18 +358,16 @@ class Model extends EventEmitter(Object) {
|
|
|
381
358
|
* Set a hash of model attributes, and sync the model to the server.
|
|
382
359
|
* If the server returns an attributes hash that differs, the model's
|
|
383
360
|
* state will be `set` again.
|
|
384
|
-
* @param {string|Attributes} [key]
|
|
385
|
-
* @param {boolean|number|string|Options} [val]
|
|
386
|
-
* @param {Options} [options]
|
|
387
361
|
*/
|
|
388
|
-
save(key
|
|
362
|
+
save(key?: string | null | Partial<T>, val?: any, options?: ModelOptions): any {
|
|
389
363
|
// Handle both `"key", value` and `{key: value}` -style arguments.
|
|
390
|
-
let attrs;
|
|
364
|
+
let attrs: Partial<T> | undefined;
|
|
391
365
|
if (key == null || typeof key === 'object') {
|
|
392
|
-
attrs = key
|
|
393
|
-
options =
|
|
366
|
+
attrs = key as Partial<T>;
|
|
367
|
+
options = val as ModelOptions;
|
|
394
368
|
} else {
|
|
395
|
-
|
|
369
|
+
attrs = {} as Partial<T>;
|
|
370
|
+
attrs[key as keyof T] = val;
|
|
396
371
|
}
|
|
397
372
|
|
|
398
373
|
options = Object.assign({ validate: true, parse: true }, options);
|
|
@@ -405,7 +380,7 @@ class Model extends EventEmitter(Object) {
|
|
|
405
380
|
// the model will be valid when the attributes, if any, are set.
|
|
406
381
|
if (attrs && !wait) {
|
|
407
382
|
if (!this.set(attrs, options)) return false;
|
|
408
|
-
} else if (!this._validate(
|
|
383
|
+
} else if (!this._validate(attrs || {}, options)) {
|
|
409
384
|
return false;
|
|
410
385
|
}
|
|
411
386
|
|
|
@@ -415,7 +390,7 @@ class Model extends EventEmitter(Object) {
|
|
|
415
390
|
const error = options.error;
|
|
416
391
|
const attributes = this.attributes;
|
|
417
392
|
|
|
418
|
-
options.success = (resp) => {
|
|
393
|
+
options.success = (resp: any) => {
|
|
419
394
|
// Ensure attributes are restored during synchronous saves.
|
|
420
395
|
this.attributes = attributes;
|
|
421
396
|
let serverAttrs = options.parse ? this.parse(resp, options) : resp;
|
|
@@ -426,7 +401,7 @@ class Model extends EventEmitter(Object) {
|
|
|
426
401
|
return_promise && promise.resolve();
|
|
427
402
|
};
|
|
428
403
|
|
|
429
|
-
options.error = (model, e, options) => {
|
|
404
|
+
options.error = (model: this, e: any, options: Options) => {
|
|
430
405
|
error && error.call(options.context, model, e, options);
|
|
431
406
|
return_promise && promise.reject(e);
|
|
432
407
|
};
|
|
@@ -454,9 +429,8 @@ class Model extends EventEmitter(Object) {
|
|
|
454
429
|
* Destroy this model on the server if it was already persisted.
|
|
455
430
|
* Optimistically removes the model from its collection, if it has one.
|
|
456
431
|
* If `wait: true` is passed, waits for the server to respond before removal.
|
|
457
|
-
* @param {Options} [options]
|
|
458
432
|
*/
|
|
459
|
-
destroy(options) {
|
|
433
|
+
destroy(options?: ModelOptions): any {
|
|
460
434
|
options = options ? clone(options) : {};
|
|
461
435
|
const success = options.success;
|
|
462
436
|
const wait = options.wait;
|
|
@@ -466,13 +440,13 @@ class Model extends EventEmitter(Object) {
|
|
|
466
440
|
this.trigger('destroy', this, this.collection, options);
|
|
467
441
|
};
|
|
468
442
|
|
|
469
|
-
options.success = (resp) => {
|
|
443
|
+
options.success = (resp: any) => {
|
|
470
444
|
if (wait) destroy();
|
|
471
445
|
if (success) success.call(options.context, this, resp, options);
|
|
472
446
|
if (!this.isNew()) this.trigger('sync', this, resp, options);
|
|
473
447
|
};
|
|
474
448
|
|
|
475
|
-
let xhr = false;
|
|
449
|
+
let xhr: any = false;
|
|
476
450
|
if (this.isNew()) {
|
|
477
451
|
defer(options.success);
|
|
478
452
|
} else {
|
|
@@ -483,51 +457,60 @@ class Model extends EventEmitter(Object) {
|
|
|
483
457
|
return xhr;
|
|
484
458
|
}
|
|
485
459
|
|
|
460
|
+
get urlRoot(): string {
|
|
461
|
+
return this._urlRoot;
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
set urlRoot(root: string) {
|
|
465
|
+
this._urlRoot = root;
|
|
466
|
+
}
|
|
467
|
+
|
|
486
468
|
/**
|
|
487
469
|
* Default URL for the model's representation on the server -- if you're
|
|
488
470
|
* using Backbone's restful methods, override this to change the endpoint
|
|
489
471
|
* that will be called.
|
|
490
472
|
*/
|
|
491
|
-
url() {
|
|
473
|
+
get url(): string {
|
|
474
|
+
if (this._url) return this._url;
|
|
475
|
+
|
|
492
476
|
const base = result(this, 'urlRoot') || result(this.collection, 'url') || urlError();
|
|
493
|
-
if (this.isNew()) return base;
|
|
477
|
+
if (this.isNew()) return base as string;
|
|
494
478
|
const id = this.get(this.idAttribute);
|
|
495
|
-
return base.replace(/[^\/]$/, '$&/') + encodeURIComponent(id);
|
|
479
|
+
return (base as string).replace(/[^\/]$/, '$&/') + encodeURIComponent(String(id));
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
set url(url: string) {
|
|
483
|
+
this._url = url;
|
|
496
484
|
}
|
|
497
485
|
|
|
498
486
|
/**
|
|
499
487
|
* **parse** converts a response into the hash of attributes to be `set` on
|
|
500
488
|
* the model. The default implementation is just to pass the response along.
|
|
501
|
-
* @param {Options} resp
|
|
502
|
-
* @param {Options} [options]
|
|
503
489
|
*/
|
|
504
|
-
parse(resp, options) {
|
|
490
|
+
parse(resp: any, options?: ModelOptions): Partial<T> | null | void {
|
|
505
491
|
return resp;
|
|
506
492
|
}
|
|
507
493
|
|
|
508
494
|
/**
|
|
509
495
|
* A model is new if it has never been saved to the server, and lacks an id.
|
|
510
496
|
*/
|
|
511
|
-
isNew() {
|
|
497
|
+
isNew(): boolean {
|
|
512
498
|
return !this.has(this.idAttribute);
|
|
513
499
|
}
|
|
514
500
|
|
|
515
501
|
/**
|
|
516
502
|
* Check if the model is currently in a valid state.
|
|
517
|
-
* @param {Options} [options]
|
|
518
503
|
*/
|
|
519
|
-
isValid(options) {
|
|
504
|
+
isValid(options?: ModelOptions): boolean {
|
|
520
505
|
return this._validate({}, Object.assign({}, options, { validate: true }));
|
|
521
506
|
}
|
|
522
507
|
|
|
523
508
|
/**
|
|
524
509
|
* Run validation against the next complete set of model attributes,
|
|
525
510
|
* returning `true` if all is well. Otherwise, fire an `"invalid"` event.
|
|
526
|
-
* @param {Attributes} attrs
|
|
527
|
-
* @param {Options} [options]
|
|
528
511
|
*/
|
|
529
|
-
_validate(attrs, options) {
|
|
530
|
-
if (!options
|
|
512
|
+
_validate(attrs: Partial<T> | ObjectWithId, options?: ModelOptions): boolean {
|
|
513
|
+
if (!options?.validate || !this.validate) return true;
|
|
531
514
|
attrs = Object.assign({}, this.attributes, attrs);
|
|
532
515
|
const error = (this.validationError = this.validate(attrs, options) || null);
|
|
533
516
|
if (!error) return true;
|
|
@@ -535,5 +518,3 @@ class Model extends EventEmitter(Object) {
|
|
|
535
518
|
return false;
|
|
536
519
|
}
|
|
537
520
|
}
|
|
538
|
-
|
|
539
|
-
export { Model };
|