@ember-data/model 4.8.0-alpha.2 → 4.8.0-alpha.3
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/addon/-private/attr.js +13 -2
- package/addon/-private/belongs-to.js +9 -3
- package/addon/-private/errors.ts +6 -6
- package/addon/-private/has-many.js +7 -3
- package/addon/-private/legacy-data-fetch.js +9 -7
- package/addon/-private/legacy-data-utils.ts +1 -1
- package/addon/-private/legacy-relationships-support.ts +33 -42
- package/addon/-private/many-array.ts +13 -7
- package/addon/-private/model.js +83 -83
- package/addon/-private/notify-changes.ts +1 -1
- package/addon/-private/promise-many-array.ts +170 -112
- package/addon/-private/record-state.ts +33 -25
- package/addon/-private/references/belongs-to.ts +5 -5
- package/addon/-private/references/has-many.ts +6 -8
- package/package.json +5 -5
package/addon/-private/model.js
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
|
|
5
5
|
import { assert, deprecate, warn } from '@ember/debug';
|
|
6
6
|
import EmberError from '@ember/error';
|
|
7
|
-
import EmberObject
|
|
7
|
+
import EmberObject from '@ember/object';
|
|
8
8
|
import { dependentKeyCompat } from '@ember/object/compat';
|
|
9
9
|
import { run } from '@ember/runloop';
|
|
10
10
|
import { inject as service } from '@ember/service';
|
|
@@ -22,7 +22,7 @@ import {
|
|
|
22
22
|
DEPRECATE_SAVE_PROMISE_ACCESS,
|
|
23
23
|
} from '@ember-data/private-build-infra/deprecations';
|
|
24
24
|
import { recordIdentifierFor, storeFor } from '@ember-data/store';
|
|
25
|
-
import { coerceId, deprecatedPromiseObject,
|
|
25
|
+
import { coerceId, deprecatedPromiseObject, recordDataFor, WeakCache } from '@ember-data/store/-private';
|
|
26
26
|
|
|
27
27
|
import Errors from './errors';
|
|
28
28
|
import { LegacySupport } from './legacy-relationships-support';
|
|
@@ -119,9 +119,10 @@ function computeOnce(target, key, desc) {
|
|
|
119
119
|
*/
|
|
120
120
|
class Model extends EmberObject {
|
|
121
121
|
@service store;
|
|
122
|
+
#notifications;
|
|
122
123
|
|
|
123
124
|
init(options = {}) {
|
|
124
|
-
if (DEBUG && !options._secretInit && !options.
|
|
125
|
+
if (DEBUG && !options._secretInit && !options._createProps) {
|
|
125
126
|
throw new EmberError(
|
|
126
127
|
'You should not call `create` on a model. Instead, call `store.createRecord` with the attributes you would like to set.'
|
|
127
128
|
);
|
|
@@ -137,19 +138,29 @@ class Model extends EmberObject {
|
|
|
137
138
|
|
|
138
139
|
this.setProperties(createProps);
|
|
139
140
|
|
|
140
|
-
// TODO pass something in such that we don't need internalModel
|
|
141
|
-
// to get this info
|
|
142
141
|
let store = storeFor(this);
|
|
143
142
|
let notifications = store._notificationManager;
|
|
144
143
|
let identity = recordIdentifierFor(this);
|
|
145
144
|
|
|
146
|
-
notifications.subscribe(identity, (identifier, type, key) => {
|
|
145
|
+
this.#notifications = notifications.subscribe(identity, (identifier, type, key) => {
|
|
147
146
|
notifyChanges(identifier, type, key, this, store);
|
|
148
147
|
});
|
|
149
148
|
}
|
|
150
149
|
|
|
151
|
-
|
|
150
|
+
destroy() {
|
|
152
151
|
LEGACY_SUPPORT.get(this)?.destroy();
|
|
152
|
+
this.___recordState?.destroy();
|
|
153
|
+
const store = storeFor(this);
|
|
154
|
+
const identifier = recordIdentifierFor(this);
|
|
155
|
+
store._notificationManager.unsubscribe(this.#notifications);
|
|
156
|
+
// Legacy behavior is to notify the relationships on destroy
|
|
157
|
+
// such that they "clear". It's uncertain this behavior would
|
|
158
|
+
// be good for a new model paradigm, likely cheaper and safer
|
|
159
|
+
// to simply not notify, for this reason the store does not itself
|
|
160
|
+
// notify individual changes once the delete has been signaled,
|
|
161
|
+
// this decision is left to model instances.
|
|
162
|
+
notifyChanges(identifier, 'relationships', undefined, this, store);
|
|
163
|
+
super.destroy();
|
|
153
164
|
}
|
|
154
165
|
|
|
155
166
|
/**
|
|
@@ -197,10 +208,10 @@ class Model extends EmberObject {
|
|
|
197
208
|
|
|
198
209
|
```javascript
|
|
199
210
|
let record = store.createRecord('model');
|
|
200
|
-
record.
|
|
211
|
+
record.isLoaded; // true
|
|
201
212
|
|
|
202
213
|
store.findRecord('model', 1).then(function(model) {
|
|
203
|
-
model.
|
|
214
|
+
model.isLoaded; // true
|
|
204
215
|
});
|
|
205
216
|
```
|
|
206
217
|
|
|
@@ -224,12 +235,12 @@ class Model extends EmberObject {
|
|
|
224
235
|
|
|
225
236
|
```javascript
|
|
226
237
|
let record = store.createRecord('model');
|
|
227
|
-
record.
|
|
238
|
+
record.hasDirtyAttributes; // true
|
|
228
239
|
|
|
229
240
|
store.findRecord('model', 1).then(function(model) {
|
|
230
|
-
model.
|
|
241
|
+
model.hasDirtyAttributes; // false
|
|
231
242
|
model.set('foo', 'some value');
|
|
232
|
-
model.
|
|
243
|
+
model.hasDirtyAttributes; // true
|
|
233
244
|
});
|
|
234
245
|
```
|
|
235
246
|
|
|
@@ -254,11 +265,11 @@ class Model extends EmberObject {
|
|
|
254
265
|
|
|
255
266
|
```javascript
|
|
256
267
|
let record = store.createRecord('model');
|
|
257
|
-
record.
|
|
268
|
+
record.isSaving; // false
|
|
258
269
|
let promise = record.save();
|
|
259
|
-
record.
|
|
270
|
+
record.isSaving; // true
|
|
260
271
|
promise.then(function() {
|
|
261
|
-
record.
|
|
272
|
+
record.isSaving; // false
|
|
262
273
|
});
|
|
263
274
|
```
|
|
264
275
|
|
|
@@ -284,24 +295,24 @@ class Model extends EmberObject {
|
|
|
284
295
|
|
|
285
296
|
```javascript
|
|
286
297
|
let record = store.createRecord('model');
|
|
287
|
-
record.
|
|
298
|
+
record.isDeleted; // false
|
|
288
299
|
record.deleteRecord();
|
|
289
300
|
|
|
290
301
|
// Locally deleted
|
|
291
|
-
record.
|
|
292
|
-
record.
|
|
293
|
-
record.
|
|
302
|
+
record.isDeleted; // true
|
|
303
|
+
record.hasDirtyAttributes; // true
|
|
304
|
+
record.isSaving; // false
|
|
294
305
|
|
|
295
306
|
// Persisting the deletion
|
|
296
307
|
let promise = record.save();
|
|
297
|
-
record.
|
|
298
|
-
record.
|
|
308
|
+
record.isDeleted; // true
|
|
309
|
+
record.isSaving; // true
|
|
299
310
|
|
|
300
311
|
// Deletion Persisted
|
|
301
312
|
promise.then(function() {
|
|
302
|
-
record.
|
|
303
|
-
record.
|
|
304
|
-
record.
|
|
313
|
+
record.isDeleted; // true
|
|
314
|
+
record.isSaving; // false
|
|
315
|
+
record.hasDirtyAttributes; // false
|
|
305
316
|
});
|
|
306
317
|
```
|
|
307
318
|
|
|
@@ -325,10 +336,10 @@ class Model extends EmberObject {
|
|
|
325
336
|
|
|
326
337
|
```javascript
|
|
327
338
|
let record = store.createRecord('model');
|
|
328
|
-
record.
|
|
339
|
+
record.isNew; // true
|
|
329
340
|
|
|
330
341
|
record.save().then(function(model) {
|
|
331
|
-
model.
|
|
342
|
+
model.isNew; // false
|
|
332
343
|
});
|
|
333
344
|
```
|
|
334
345
|
|
|
@@ -371,7 +382,7 @@ class Model extends EmberObject {
|
|
|
371
382
|
|
|
372
383
|
```javascript
|
|
373
384
|
let record = store.createRecord('model');
|
|
374
|
-
record.
|
|
385
|
+
record.dirtyType; // 'created'
|
|
375
386
|
```
|
|
376
387
|
|
|
377
388
|
@property dirtyType
|
|
@@ -392,10 +403,10 @@ class Model extends EmberObject {
|
|
|
392
403
|
Example
|
|
393
404
|
|
|
394
405
|
```javascript
|
|
395
|
-
record.
|
|
406
|
+
record.isError; // false
|
|
396
407
|
record.set('foo', 'valid value');
|
|
397
408
|
record.save().then(null, function() {
|
|
398
|
-
record.
|
|
409
|
+
record.isError; // true
|
|
399
410
|
});
|
|
400
411
|
```
|
|
401
412
|
|
|
@@ -441,10 +452,10 @@ class Model extends EmberObject {
|
|
|
441
452
|
|
|
442
453
|
```javascript
|
|
443
454
|
let record = store.createRecord('model');
|
|
444
|
-
record.
|
|
455
|
+
record.id; // null
|
|
445
456
|
|
|
446
457
|
store.findRecord('model', 1).then(function(model) {
|
|
447
|
-
model.
|
|
458
|
+
model.id; // '1'
|
|
448
459
|
});
|
|
449
460
|
```
|
|
450
461
|
|
|
@@ -454,24 +465,38 @@ class Model extends EmberObject {
|
|
|
454
465
|
*/
|
|
455
466
|
@tagged
|
|
456
467
|
get id() {
|
|
457
|
-
//
|
|
468
|
+
// this guard exists, because some dev-only deprecation code
|
|
458
469
|
// (addListener via validatePropertyInjections) invokes toString before the
|
|
459
470
|
// object is real.
|
|
460
471
|
if (DEBUG) {
|
|
461
|
-
|
|
472
|
+
try {
|
|
473
|
+
return recordIdentifierFor(this).id;
|
|
474
|
+
} catch {
|
|
462
475
|
return void 0;
|
|
463
476
|
}
|
|
464
477
|
}
|
|
465
|
-
return this.
|
|
478
|
+
return recordIdentifierFor(this).id;
|
|
466
479
|
}
|
|
467
480
|
set id(id) {
|
|
468
481
|
const normalizedId = coerceId(id);
|
|
482
|
+
const identifier = recordIdentifierFor(this);
|
|
483
|
+
let didChange = normalizedId !== identifier.id;
|
|
484
|
+
assert(
|
|
485
|
+
`Cannot set ${identifier.type} record's id to ${id}, because id is already ${identifier.id}`,
|
|
486
|
+
!didChange || identifier.id === null
|
|
487
|
+
);
|
|
469
488
|
|
|
470
|
-
if (normalizedId !== null) {
|
|
471
|
-
this.
|
|
489
|
+
if (normalizedId !== null && didChange) {
|
|
490
|
+
this.store._instanceCache.setRecordId(identifier.type, normalizedId, identifier.lid);
|
|
491
|
+
this.store._notificationManager.notify(identifier, 'identity');
|
|
472
492
|
}
|
|
473
493
|
}
|
|
474
494
|
|
|
495
|
+
// TODO just write a nice toString
|
|
496
|
+
toStringExtension() {
|
|
497
|
+
return this.id;
|
|
498
|
+
}
|
|
499
|
+
|
|
475
500
|
/**
|
|
476
501
|
@property currentState
|
|
477
502
|
@private
|
|
@@ -494,12 +519,6 @@ class Model extends EmberObject {
|
|
|
494
519
|
throw new Error('cannot set currentState');
|
|
495
520
|
}
|
|
496
521
|
|
|
497
|
-
/**
|
|
498
|
-
@property _internalModel
|
|
499
|
-
@private
|
|
500
|
-
@type {Object}
|
|
501
|
-
*/
|
|
502
|
-
|
|
503
522
|
/**
|
|
504
523
|
The store service instance which created this record instance
|
|
505
524
|
|
|
@@ -517,10 +536,10 @@ class Model extends EmberObject {
|
|
|
517
536
|
- `attribute` The name of the property associated with this error message
|
|
518
537
|
|
|
519
538
|
```javascript
|
|
520
|
-
record.
|
|
539
|
+
record.errors.length; // 0
|
|
521
540
|
record.set('foo', 'invalid value');
|
|
522
541
|
record.save().catch(function() {
|
|
523
|
-
record.
|
|
542
|
+
record.errors.foo;
|
|
524
543
|
// [{message: 'foo should be a number.', attribute: 'foo'}]
|
|
525
544
|
});
|
|
526
545
|
```
|
|
@@ -794,7 +813,7 @@ class Model extends EmberObject {
|
|
|
794
813
|
and value is an [oldProp, newProp] array.
|
|
795
814
|
*/
|
|
796
815
|
changedAttributes() {
|
|
797
|
-
return this.
|
|
816
|
+
return recordDataFor(this).changedAttributes();
|
|
798
817
|
}
|
|
799
818
|
|
|
800
819
|
/**
|
|
@@ -804,11 +823,11 @@ class Model extends EmberObject {
|
|
|
804
823
|
Example
|
|
805
824
|
|
|
806
825
|
```javascript
|
|
807
|
-
record.
|
|
826
|
+
record.name; // 'Untitled Document'
|
|
808
827
|
record.set('name', 'Doc 1');
|
|
809
|
-
record.
|
|
828
|
+
record.name; // 'Doc 1'
|
|
810
829
|
record.rollbackAttributes();
|
|
811
|
-
record.
|
|
830
|
+
record.name; // 'Untitled Document'
|
|
812
831
|
```
|
|
813
832
|
|
|
814
833
|
@since 1.13.0
|
|
@@ -817,8 +836,13 @@ class Model extends EmberObject {
|
|
|
817
836
|
*/
|
|
818
837
|
rollbackAttributes() {
|
|
819
838
|
const { currentState } = this;
|
|
820
|
-
|
|
839
|
+
const { isNew } = currentState;
|
|
840
|
+
recordDataFor(this).rollbackAttributes();
|
|
841
|
+
this.errors.clear();
|
|
821
842
|
currentState.cleanErrorRequests();
|
|
843
|
+
if (isNew) {
|
|
844
|
+
this.unloadRecord();
|
|
845
|
+
}
|
|
822
846
|
}
|
|
823
847
|
|
|
824
848
|
/**
|
|
@@ -830,13 +854,6 @@ class Model extends EmberObject {
|
|
|
830
854
|
return storeFor(this)._instanceCache.createSnapshot(recordIdentifierFor(this));
|
|
831
855
|
}
|
|
832
856
|
|
|
833
|
-
toStringExtension() {
|
|
834
|
-
// the _internalModel guard exists, because some dev-only deprecation code
|
|
835
|
-
// (addListener via validatePropertyInjections) invokes toString before the
|
|
836
|
-
// object is real.
|
|
837
|
-
return this._internalModel && this._internalModel.id;
|
|
838
|
-
}
|
|
839
|
-
|
|
840
857
|
/**
|
|
841
858
|
Save the record and persist any changes to the record to an
|
|
842
859
|
external source via the adapter.
|
|
@@ -1491,10 +1508,10 @@ class Model extends EmberObject {
|
|
|
1491
1508
|
import Post from 'app/models/post';
|
|
1492
1509
|
|
|
1493
1510
|
let relationships = Blog.relationships;
|
|
1494
|
-
relationships.
|
|
1511
|
+
relationships.user;
|
|
1495
1512
|
//=> [ { name: 'users', kind: 'hasMany' },
|
|
1496
1513
|
// { name: 'owner', kind: 'belongsTo' } ]
|
|
1497
|
-
relationships.
|
|
1514
|
+
relationships.post;
|
|
1498
1515
|
//=> [ { name: 'posts', kind: 'hasMany' } ]
|
|
1499
1516
|
```
|
|
1500
1517
|
|
|
@@ -1707,9 +1724,9 @@ class Model extends EmberObject {
|
|
|
1707
1724
|
import Blog from 'app/models/blog';
|
|
1708
1725
|
|
|
1709
1726
|
let relationshipsByName = Blog.relationshipsByName;
|
|
1710
|
-
relationshipsByName.
|
|
1727
|
+
relationshipsByName.users;
|
|
1711
1728
|
//=> { key: 'users', kind: 'hasMany', type: 'user', options: Object, isRelationship: true }
|
|
1712
|
-
relationshipsByName.
|
|
1729
|
+
relationshipsByName.owner;
|
|
1713
1730
|
//=> { key: 'owner', kind: 'belongsTo', type: 'user', options: Object, isRelationship: true }
|
|
1714
1731
|
```
|
|
1715
1732
|
|
|
@@ -1810,7 +1827,7 @@ class Model extends EmberObject {
|
|
|
1810
1827
|
|
|
1811
1828
|
let fields = Blog.fields;
|
|
1812
1829
|
fields.forEach(function(kind, field) {
|
|
1813
|
-
|
|
1830
|
+
// do thing
|
|
1814
1831
|
});
|
|
1815
1832
|
|
|
1816
1833
|
// prints:
|
|
@@ -1992,7 +2009,7 @@ class Model extends EmberObject {
|
|
|
1992
2009
|
let attributes = Person.attributes
|
|
1993
2010
|
|
|
1994
2011
|
attributes.forEach(function(meta, name) {
|
|
1995
|
-
|
|
2012
|
+
// do thing
|
|
1996
2013
|
});
|
|
1997
2014
|
|
|
1998
2015
|
// prints:
|
|
@@ -2069,7 +2086,7 @@ class Model extends EmberObject {
|
|
|
2069
2086
|
let transformedAttributes = Person.transformedAttributes
|
|
2070
2087
|
|
|
2071
2088
|
transformedAttributes.forEach(function(field, type) {
|
|
2072
|
-
|
|
2089
|
+
// do thing
|
|
2073
2090
|
});
|
|
2074
2091
|
|
|
2075
2092
|
// prints:
|
|
@@ -2142,7 +2159,7 @@ class Model extends EmberObject {
|
|
|
2142
2159
|
}
|
|
2143
2160
|
|
|
2144
2161
|
PersonModel.eachAttribute(function(name, meta) {
|
|
2145
|
-
|
|
2162
|
+
// do thing
|
|
2146
2163
|
});
|
|
2147
2164
|
|
|
2148
2165
|
// prints:
|
|
@@ -2211,7 +2228,7 @@ class Model extends EmberObject {
|
|
|
2211
2228
|
});
|
|
2212
2229
|
|
|
2213
2230
|
Person.eachTransformedAttribute(function(name, type) {
|
|
2214
|
-
|
|
2231
|
+
// do thing
|
|
2215
2232
|
});
|
|
2216
2233
|
|
|
2217
2234
|
// prints:
|
|
@@ -2273,13 +2290,12 @@ class Model extends EmberObject {
|
|
|
2273
2290
|
this.modelName
|
|
2274
2291
|
);
|
|
2275
2292
|
}
|
|
2276
|
-
return `model:${
|
|
2293
|
+
return `model:${this.modelName}`;
|
|
2277
2294
|
}
|
|
2278
2295
|
}
|
|
2279
2296
|
|
|
2280
2297
|
// this is required to prevent `init` from passing
|
|
2281
2298
|
// the values initialized during create to `setUnknownProperty`
|
|
2282
|
-
Model.prototype._internalModel = null;
|
|
2283
2299
|
Model.prototype._createProps = null;
|
|
2284
2300
|
Model.prototype._secretInit = null;
|
|
2285
2301
|
|
|
@@ -2359,27 +2375,11 @@ if (DEBUG) {
|
|
|
2359
2375
|
} while (current !== null);
|
|
2360
2376
|
return null;
|
|
2361
2377
|
};
|
|
2362
|
-
let isBasicDesc = function isBasicDesc(desc) {
|
|
2363
|
-
return (
|
|
2364
|
-
!desc ||
|
|
2365
|
-
(!desc.get && !desc.set && desc.enumerable === true && desc.writable === true && desc.configurable === true)
|
|
2366
|
-
);
|
|
2367
|
-
};
|
|
2368
|
-
let isDefaultEmptyDescriptor = function isDefaultEmptyDescriptor(obj, keyName) {
|
|
2369
|
-
let instanceDesc = lookupDescriptor(obj, keyName);
|
|
2370
|
-
return isBasicDesc(instanceDesc) && lookupDescriptor(obj.constructor, keyName) === null;
|
|
2371
|
-
};
|
|
2372
2378
|
|
|
2373
2379
|
Model.reopen({
|
|
2374
2380
|
init() {
|
|
2375
2381
|
this._super(...arguments);
|
|
2376
2382
|
|
|
2377
|
-
if (!isDefaultEmptyDescriptor(this, '_internalModel') || !(this._internalModel instanceof InternalModel)) {
|
|
2378
|
-
throw new Error(
|
|
2379
|
-
`'_internalModel' is a reserved property name on instances of classes extending Model. Please choose a different property name for ${this.constructor.toString()}`
|
|
2380
|
-
);
|
|
2381
|
-
}
|
|
2382
|
-
|
|
2383
2383
|
let ourDescriptor = lookupDescriptor(Model.prototype, 'currentState');
|
|
2384
2384
|
let theirDescriptor = lookupDescriptor(this, 'currentState');
|
|
2385
2385
|
let realState = this.___recordState;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { cacheFor } from '@ember/object/internals';
|
|
2
2
|
|
|
3
3
|
import type Store from '@ember-data/store';
|
|
4
|
-
import type { NotificationType } from '@ember-data/store/-private/record-notification-manager';
|
|
4
|
+
import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
|
|
5
5
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
6
6
|
|
|
7
7
|
import type Model from './model';
|