@ember-data/model 4.8.0-alpha.2 → 4.8.0-alpha.5
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 +9 -24
- package/addon/-private/belongs-to.js +87 -25
- package/addon/-private/errors.ts +6 -6
- package/addon/-private/has-many.js +81 -21
- package/addon/-private/legacy-data-fetch.js +35 -13
- package/addon/-private/legacy-data-utils.ts +1 -1
- package/addon/-private/legacy-relationships-support.ts +138 -133
- package/addon/-private/many-array.ts +38 -26
- package/addon/-private/model.js +105 -99
- package/addon/-private/notify-changes.ts +2 -2
- package/addon/-private/promise-many-array.ts +170 -112
- package/addon/-private/record-state.ts +35 -33
- package/addon/-private/references/belongs-to.ts +44 -36
- package/addon/-private/references/has-many.ts +65 -54
- package/addon/-private/relationship-meta.ts +3 -23
- package/package.json +12 -8
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import ArrayMixin, { NativeArray } from '@ember/array';
|
|
2
2
|
import type ArrayProxy from '@ember/array/proxy';
|
|
3
|
-
import { assert } from '@ember/debug';
|
|
3
|
+
import { assert, deprecate } from '@ember/debug';
|
|
4
4
|
import { dependentKeyCompat } from '@ember/object/compat';
|
|
5
5
|
import { tracked } from '@glimmer/tracking';
|
|
6
6
|
import Ember from 'ember';
|
|
@@ -9,8 +9,10 @@ import { resolve } from 'rsvp';
|
|
|
9
9
|
|
|
10
10
|
import type { ManyArray } from 'ember-data/-private';
|
|
11
11
|
|
|
12
|
-
import
|
|
12
|
+
import { DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS } from '@ember-data/private-build-infra/deprecations';
|
|
13
|
+
import { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
13
14
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
15
|
+
import { FindOptions } from '@ember-data/types/q/store';
|
|
14
16
|
|
|
15
17
|
export interface HasManyProxyCreateArgs {
|
|
16
18
|
promise: Promise<ManyArray>;
|
|
@@ -24,16 +26,10 @@ export interface HasManyProxyCreateArgs {
|
|
|
24
26
|
This class is returned as the result of accessing an async hasMany relationship
|
|
25
27
|
on an instance of a Model extending from `@ember-data/model`.
|
|
26
28
|
|
|
27
|
-
A PromiseManyArray is an
|
|
28
|
-
|
|
29
|
+
A PromiseManyArray is an iterable proxy that allows templates to consume related
|
|
30
|
+
ManyArrays and update once their contents are no longer pending.
|
|
29
31
|
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
* `reload()`
|
|
33
|
-
* `createRecord()`
|
|
34
|
-
|
|
35
|
-
This promise-proxy behavior is primarily to ensure that async relationship interact
|
|
36
|
-
nicely with templates. In your JS code you should resolve the promise first.
|
|
32
|
+
In your JS code you should resolve the promise first.
|
|
37
33
|
|
|
38
34
|
```js
|
|
39
35
|
const comments = await post.comments;
|
|
@@ -42,10 +38,14 @@ export interface HasManyProxyCreateArgs {
|
|
|
42
38
|
@class PromiseManyArray
|
|
43
39
|
@public
|
|
44
40
|
*/
|
|
45
|
-
export default interface PromiseManyArray extends Omit<ArrayProxy<
|
|
41
|
+
export default interface PromiseManyArray extends Omit<ArrayProxy<StableRecordIdentifier, RecordInstance>, 'destroy'> {
|
|
42
|
+
createRecord(): RecordInstance;
|
|
43
|
+
reload(options: FindOptions): PromiseManyArray;
|
|
44
|
+
}
|
|
46
45
|
export default class PromiseManyArray {
|
|
47
46
|
declare promise: Promise<ManyArray> | null;
|
|
48
47
|
declare isDestroyed: boolean;
|
|
48
|
+
// @deprecated (isDestroyed is not deprecated)
|
|
49
49
|
declare isDestroying: boolean;
|
|
50
50
|
|
|
51
51
|
constructor(promise: Promise<ManyArray>, content?: ManyArray) {
|
|
@@ -90,6 +90,8 @@ export default class PromiseManyArray {
|
|
|
90
90
|
|
|
91
91
|
/**
|
|
92
92
|
* Iterate the proxied content. Called by the glimmer iterator in #each
|
|
93
|
+
* We do not guarantee that forEach will always be available. This
|
|
94
|
+
* may eventually be made to use Symbol.Iterator once glimmer supports it.
|
|
93
95
|
*
|
|
94
96
|
* @method forEach
|
|
95
97
|
* @param cb
|
|
@@ -103,6 +105,19 @@ export default class PromiseManyArray {
|
|
|
103
105
|
}
|
|
104
106
|
}
|
|
105
107
|
|
|
108
|
+
/**
|
|
109
|
+
* Reload the relationship
|
|
110
|
+
* @method reload
|
|
111
|
+
* @public
|
|
112
|
+
* @param options
|
|
113
|
+
* @returns
|
|
114
|
+
*/
|
|
115
|
+
reload(options: FindOptions) {
|
|
116
|
+
assert('You are trying to reload an async manyArray before it has been created', this.content);
|
|
117
|
+
this.content.reload(options);
|
|
118
|
+
return this;
|
|
119
|
+
}
|
|
120
|
+
|
|
106
121
|
//---- Properties/Methods from the PromiseProxyMixin that we will keep as our API
|
|
107
122
|
|
|
108
123
|
/**
|
|
@@ -201,19 +216,6 @@ export default class PromiseManyArray {
|
|
|
201
216
|
return this.content ? this.content.meta : undefined;
|
|
202
217
|
}
|
|
203
218
|
|
|
204
|
-
/**
|
|
205
|
-
* Reload the relationship
|
|
206
|
-
* @method reload
|
|
207
|
-
* @public
|
|
208
|
-
* @param options
|
|
209
|
-
* @returns
|
|
210
|
-
*/
|
|
211
|
-
reload(options) {
|
|
212
|
-
assert('You are trying to reload an async manyArray before it has been created', this.content);
|
|
213
|
-
this.content.reload(options);
|
|
214
|
-
return this;
|
|
215
|
-
}
|
|
216
|
-
|
|
217
219
|
//---- Our own stuff
|
|
218
220
|
|
|
219
221
|
_update(promise: Promise<ManyArray>, content?: ManyArray) {
|
|
@@ -227,22 +229,55 @@ export default class PromiseManyArray {
|
|
|
227
229
|
static create({ promise, content }: HasManyProxyCreateArgs): PromiseManyArray {
|
|
228
230
|
return new this(promise, content);
|
|
229
231
|
}
|
|
232
|
+
}
|
|
230
233
|
|
|
231
|
-
|
|
232
|
-
createRecord(...args) {
|
|
234
|
+
if (DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS) {
|
|
235
|
+
PromiseManyArray.prototype.createRecord = function createRecord(...args) {
|
|
236
|
+
deprecate(
|
|
237
|
+
`The createRecord method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
|
|
238
|
+
false,
|
|
239
|
+
{
|
|
240
|
+
id: 'ember-data:deprecate-promise-many-array-behaviors',
|
|
241
|
+
until: '5.0',
|
|
242
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
243
|
+
for: 'ember-data',
|
|
244
|
+
}
|
|
245
|
+
);
|
|
233
246
|
assert('You are trying to createRecord on an async manyArray before it has been created', this.content);
|
|
234
247
|
return this.content.createRecord(...args);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Properties/Methods on ArrayProxy we should deprecate
|
|
238
|
-
|
|
239
|
-
get firstObject() {
|
|
240
|
-
return this.content ? this.content.firstObject : undefined;
|
|
241
|
-
}
|
|
248
|
+
};
|
|
242
249
|
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
250
|
+
Object.defineProperty(PromiseManyArray.prototype, 'firstObject', {
|
|
251
|
+
get() {
|
|
252
|
+
deprecate(
|
|
253
|
+
`The firstObject property on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
|
|
254
|
+
false,
|
|
255
|
+
{
|
|
256
|
+
id: 'ember-data:deprecate-promise-many-array-behaviors',
|
|
257
|
+
until: '5.0',
|
|
258
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
259
|
+
for: 'ember-data',
|
|
260
|
+
}
|
|
261
|
+
);
|
|
262
|
+
return this.content ? this.content.firstObject : undefined;
|
|
263
|
+
},
|
|
264
|
+
});
|
|
265
|
+
|
|
266
|
+
Object.defineProperty(PromiseManyArray.prototype, 'lastObject', {
|
|
267
|
+
get() {
|
|
268
|
+
deprecate(
|
|
269
|
+
`The lastObject property on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
|
|
270
|
+
false,
|
|
271
|
+
{
|
|
272
|
+
id: 'ember-data:deprecate-promise-many-array-behaviors',
|
|
273
|
+
until: '5.0',
|
|
274
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
275
|
+
for: 'ember-data',
|
|
276
|
+
}
|
|
277
|
+
);
|
|
278
|
+
return this.content ? this.content.lastObject : undefined;
|
|
279
|
+
},
|
|
280
|
+
});
|
|
246
281
|
}
|
|
247
282
|
|
|
248
283
|
function tapPromise(proxy: PromiseManyArray, promise: Promise<ManyArray>) {
|
|
@@ -268,78 +303,101 @@ function tapPromise(proxy: PromiseManyArray, promise: Promise<ManyArray>) {
|
|
|
268
303
|
);
|
|
269
304
|
}
|
|
270
305
|
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
}
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
306
|
+
if (DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS) {
|
|
307
|
+
const EmberObjectMethods = [
|
|
308
|
+
'addObserver',
|
|
309
|
+
'cacheFor',
|
|
310
|
+
'decrementProperty',
|
|
311
|
+
'get',
|
|
312
|
+
'getProperties',
|
|
313
|
+
'incrementProperty',
|
|
314
|
+
'notifyPropertyChange',
|
|
315
|
+
'removeObserver',
|
|
316
|
+
'set',
|
|
317
|
+
'setProperties',
|
|
318
|
+
'toggleProperty',
|
|
319
|
+
];
|
|
320
|
+
EmberObjectMethods.forEach((method) => {
|
|
321
|
+
PromiseManyArray.prototype[method] = function delegatedMethod(...args) {
|
|
322
|
+
deprecate(
|
|
323
|
+
`The ${method} method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
|
|
324
|
+
false,
|
|
325
|
+
{
|
|
326
|
+
id: 'ember-data:deprecate-promise-many-array-behaviors',
|
|
327
|
+
until: '5.0',
|
|
328
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
329
|
+
for: 'ember-data',
|
|
330
|
+
}
|
|
331
|
+
);
|
|
332
|
+
return Ember[method](this, ...args);
|
|
333
|
+
};
|
|
334
|
+
});
|
|
335
|
+
|
|
336
|
+
const InheritedProxyMethods = [
|
|
337
|
+
'addArrayObserver',
|
|
338
|
+
'addObject',
|
|
339
|
+
'addObjects',
|
|
340
|
+
'any',
|
|
341
|
+
'arrayContentDidChange',
|
|
342
|
+
'arrayContentWillChange',
|
|
343
|
+
'clear',
|
|
344
|
+
'compact',
|
|
345
|
+
'every',
|
|
346
|
+
'filter',
|
|
347
|
+
'filterBy',
|
|
348
|
+
'find',
|
|
349
|
+
'findBy',
|
|
350
|
+
'getEach',
|
|
351
|
+
'includes',
|
|
352
|
+
'indexOf',
|
|
353
|
+
'insertAt',
|
|
354
|
+
'invoke',
|
|
355
|
+
'isAny',
|
|
356
|
+
'isEvery',
|
|
357
|
+
'lastIndexOf',
|
|
358
|
+
'map',
|
|
359
|
+
'mapBy',
|
|
360
|
+
// TODO update RFC to note objectAt was deprecated (forEach was left for iteration)
|
|
361
|
+
'objectAt',
|
|
362
|
+
'objectsAt',
|
|
363
|
+
'popObject',
|
|
364
|
+
'pushObject',
|
|
365
|
+
'pushObjects',
|
|
366
|
+
'reduce',
|
|
367
|
+
'reject',
|
|
368
|
+
'rejectBy',
|
|
369
|
+
'removeArrayObserver',
|
|
370
|
+
'removeAt',
|
|
371
|
+
'removeObject',
|
|
372
|
+
'removeObjects',
|
|
373
|
+
'replace',
|
|
374
|
+
'reverseObjects',
|
|
375
|
+
'setEach',
|
|
376
|
+
'setObjects',
|
|
377
|
+
'shiftObject',
|
|
378
|
+
'slice',
|
|
379
|
+
'sortBy',
|
|
380
|
+
'toArray',
|
|
381
|
+
'uniq',
|
|
382
|
+
'uniqBy',
|
|
383
|
+
'unshiftObject',
|
|
384
|
+
'unshiftObjects',
|
|
385
|
+
'without',
|
|
386
|
+
];
|
|
387
|
+
InheritedProxyMethods.forEach((method) => {
|
|
388
|
+
PromiseManyArray.prototype[method] = function proxiedMethod(...args) {
|
|
389
|
+
deprecate(
|
|
390
|
+
`The ${method} method on ember-data's PromiseManyArray is deprecated. await the promise and work with the ManyArray directly.`,
|
|
391
|
+
false,
|
|
392
|
+
{
|
|
393
|
+
id: 'ember-data:deprecate-promise-many-array-behaviors',
|
|
394
|
+
until: '5.0',
|
|
395
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
396
|
+
for: 'ember-data',
|
|
397
|
+
}
|
|
398
|
+
);
|
|
399
|
+
assert(`Cannot call ${method} before content is assigned.`, this.content);
|
|
400
|
+
return this.content[method](...args);
|
|
401
|
+
};
|
|
402
|
+
});
|
|
403
|
+
}
|
|
@@ -6,8 +6,8 @@ import { cached, tracked } from '@glimmer/tracking';
|
|
|
6
6
|
import type Store from '@ember-data/store';
|
|
7
7
|
import { storeFor } from '@ember-data/store';
|
|
8
8
|
import { recordIdentifierFor } from '@ember-data/store/-private';
|
|
9
|
-
import type { NotificationType } from '@ember-data/store/-private/record-notification-manager';
|
|
10
|
-
import type RequestCache from '@ember-data/store/-private/request-cache';
|
|
9
|
+
import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
|
|
10
|
+
import type RequestCache from '@ember-data/store/-private/network/request-cache';
|
|
11
11
|
import type { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
12
12
|
import type { RecordData } from '@ember-data/types/q/record-data';
|
|
13
13
|
|
|
@@ -104,8 +104,9 @@ export function tagged(_target, key, desc) {
|
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
|
-
Historically
|
|
108
|
-
the
|
|
107
|
+
Historically EmberData managed a state machine
|
|
108
|
+
for each record, the localState for which
|
|
109
|
+
was reflected onto Model.
|
|
109
110
|
|
|
110
111
|
This implements the flags and stateName for backwards compat
|
|
111
112
|
with the state tree that used to be possible (listed below).
|
|
@@ -153,6 +154,7 @@ export default class RecordState {
|
|
|
153
154
|
declare recordData: RecordData;
|
|
154
155
|
declare _errorRequests: any[];
|
|
155
156
|
declare _lastError: any;
|
|
157
|
+
declare handler: object;
|
|
156
158
|
|
|
157
159
|
constructor(record: Model) {
|
|
158
160
|
const store = storeFor(record)!;
|
|
@@ -232,27 +234,30 @@ export default class RecordState {
|
|
|
232
234
|
}
|
|
233
235
|
}
|
|
234
236
|
|
|
235
|
-
notifications.subscribe(
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
break;
|
|
237
|
+
this.handler = notifications.subscribe(
|
|
238
|
+
identity,
|
|
239
|
+
(identifier: StableRecordIdentifier, type: NotificationType, key?: string) => {
|
|
240
|
+
switch (type) {
|
|
241
|
+
case 'state':
|
|
242
|
+
this.notify('isNew');
|
|
243
|
+
this.notify('isDeleted');
|
|
244
|
+
this.notify('isDirty');
|
|
245
|
+
break;
|
|
246
|
+
case 'attributes':
|
|
247
|
+
this.notify('isEmpty');
|
|
248
|
+
this.notify('isDirty');
|
|
249
|
+
break;
|
|
250
|
+
case 'errors':
|
|
251
|
+
this.updateInvalidErrors(this.record.errors);
|
|
252
|
+
this.notify('isValid');
|
|
253
|
+
break;
|
|
254
|
+
}
|
|
254
255
|
}
|
|
255
|
-
|
|
256
|
+
);
|
|
257
|
+
}
|
|
258
|
+
|
|
259
|
+
destroy() {
|
|
260
|
+
storeFor(this.record)!._notificationManager.unsubscribe(this.handler);
|
|
256
261
|
}
|
|
257
262
|
|
|
258
263
|
notify(key) {
|
|
@@ -304,7 +309,6 @@ export default class RecordState {
|
|
|
304
309
|
return !this.isLoaded && this.pendingCount > 0 && this.fulfilledCount === 0;
|
|
305
310
|
}
|
|
306
311
|
|
|
307
|
-
// TODO @runspired handle "unloadRecord" see note in InternalModel
|
|
308
312
|
@tagged
|
|
309
313
|
get isLoaded() {
|
|
310
314
|
if (this.isNew) {
|
|
@@ -318,7 +322,7 @@ export default class RecordState {
|
|
|
318
322
|
let rd = this.recordData;
|
|
319
323
|
if (this.isDeleted) {
|
|
320
324
|
assert(`Expected RecordData to implement isDeletionCommitted()`, rd.isDeletionCommitted);
|
|
321
|
-
return rd.isDeletionCommitted();
|
|
325
|
+
return rd.isDeletionCommitted(this.identifier);
|
|
322
326
|
}
|
|
323
327
|
if (this.isNew || this.isEmpty || !this.isValid || this.isDirty || this.isLoading) {
|
|
324
328
|
return false;
|
|
@@ -332,21 +336,21 @@ export default class RecordState {
|
|
|
332
336
|
// TODO this is not actually an RFC'd concept. Determine the
|
|
333
337
|
// correct heuristic to replace this with.
|
|
334
338
|
assert(`Expected RecordData to implement isEmpty()`, rd.isEmpty);
|
|
335
|
-
return !this.isNew && rd.isEmpty();
|
|
339
|
+
return !this.isNew && rd.isEmpty(this.identifier);
|
|
336
340
|
}
|
|
337
341
|
|
|
338
342
|
@tagged
|
|
339
343
|
get isNew() {
|
|
340
344
|
let rd = this.recordData;
|
|
341
345
|
assert(`Expected RecordData to implement isNew()`, rd.isNew);
|
|
342
|
-
return rd.isNew();
|
|
346
|
+
return rd.isNew(this.identifier);
|
|
343
347
|
}
|
|
344
348
|
|
|
345
349
|
@tagged
|
|
346
350
|
get isDeleted() {
|
|
347
351
|
let rd = this.recordData;
|
|
348
352
|
assert(`Expected RecordData to implement isDeleted()`, rd.isDeleted);
|
|
349
|
-
return rd.isDeleted();
|
|
353
|
+
return rd.isDeleted(this.identifier);
|
|
350
354
|
}
|
|
351
355
|
|
|
352
356
|
@tagged
|
|
@@ -357,12 +361,10 @@ export default class RecordState {
|
|
|
357
361
|
@tagged
|
|
358
362
|
get isDirty() {
|
|
359
363
|
let rd = this.recordData;
|
|
360
|
-
|
|
361
|
-
assert(`Expected RecordData to implement isDeletionCommitted()`, rd.isDeletionCommitted);
|
|
362
|
-
if (rd.isDeletionCommitted() || (this.isDeleted && this.isNew)) {
|
|
364
|
+
if (rd.isDeletionCommitted(this.identifier) || (this.isDeleted && this.isNew)) {
|
|
363
365
|
return false;
|
|
364
366
|
}
|
|
365
|
-
return this.isNew || rd.
|
|
367
|
+
return this.isNew || rd.hasChangedAttrs(this.identifier);
|
|
366
368
|
}
|
|
367
369
|
|
|
368
370
|
@tagged
|