@ember-data/model 4.6.1 → 4.7.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/addon/-private/attr.js +9 -24
- package/addon/-private/belongs-to.js +87 -25
- package/addon/-private/deprecated-promise-proxy.ts +54 -0
- package/addon/-private/errors.ts +6 -6
- package/addon/-private/has-many.js +84 -22
- 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 +279 -218
- package/addon/-private/many-array.ts +216 -242
- package/addon/-private/model-for-mixin.ts +2 -4
- package/addon/-private/model.js +565 -172
- package/addon/-private/notify-changes.ts +2 -2
- package/addon/-private/promise-belongs-to.ts +1 -1
- package/addon/-private/promise-many-array.ts +204 -124
- package/addon/-private/promise-proxy-base.js +4 -0
- package/addon/-private/record-state.ts +63 -46
- package/addon/-private/references/belongs-to.ts +65 -38
- package/addon/-private/references/has-many.ts +86 -55
- package/addon/-private/relationship-meta.ts +8 -26
- package/index.js +3 -0
- package/package.json +12 -8
|
@@ -1,15 +1,17 @@
|
|
|
1
|
+
import { deprecate } from '@ember/debug';
|
|
1
2
|
import { dependentKeyCompat } from '@ember/object/compat';
|
|
2
3
|
import { cached, tracked } from '@glimmer/tracking';
|
|
3
4
|
|
|
4
5
|
import type { Object as JSONObject, Value as JSONValue } from 'json-typescript';
|
|
5
6
|
import { resolve } from 'rsvp';
|
|
6
7
|
|
|
7
|
-
import
|
|
8
|
+
import { DEPRECATE_PROMISE_PROXIES } from '@ember-data/private-build-infra/deprecations';
|
|
9
|
+
import type { Graph } from '@ember-data/record-data/-private/graph/graph';
|
|
10
|
+
import type BelongsToRelationship from '@ember-data/record-data/-private/relationships/state/belongs-to';
|
|
8
11
|
import type Store from '@ember-data/store';
|
|
9
12
|
import { assertPolymorphicType } from '@ember-data/store/-debug';
|
|
10
13
|
import { recordIdentifierFor } from '@ember-data/store/-private';
|
|
11
|
-
import type { NotificationType } from '@ember-data/store/-private/record-notification-manager';
|
|
12
|
-
import type { DebugWeakCache } from '@ember-data/store/-private/weak-cache';
|
|
14
|
+
import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
|
|
13
15
|
import type {
|
|
14
16
|
LinkObject,
|
|
15
17
|
Links,
|
|
@@ -52,31 +54,34 @@ export default class BelongsToReference {
|
|
|
52
54
|
declare key: string;
|
|
53
55
|
declare belongsToRelationship: BelongsToRelationship;
|
|
54
56
|
declare type: string;
|
|
55
|
-
|
|
57
|
+
___identifier: StableRecordIdentifier;
|
|
56
58
|
declare store: Store;
|
|
59
|
+
declare graph: Graph;
|
|
57
60
|
|
|
58
61
|
// unsubscribe tokens given to us by the notification manager
|
|
59
|
-
|
|
60
|
-
|
|
62
|
+
___token!: object;
|
|
63
|
+
___relatedToken: object | null = null;
|
|
61
64
|
|
|
62
65
|
@tracked _ref = 0;
|
|
63
66
|
|
|
64
67
|
constructor(
|
|
65
68
|
store: Store,
|
|
69
|
+
graph: Graph,
|
|
66
70
|
parentIdentifier: StableRecordIdentifier,
|
|
67
71
|
belongsToRelationship: BelongsToRelationship,
|
|
68
72
|
key: string
|
|
69
73
|
) {
|
|
74
|
+
this.graph = graph;
|
|
70
75
|
this.key = key;
|
|
71
76
|
this.belongsToRelationship = belongsToRelationship;
|
|
72
77
|
this.type = belongsToRelationship.definition.type;
|
|
73
78
|
this.store = store;
|
|
74
|
-
this
|
|
79
|
+
this.___identifier = parentIdentifier;
|
|
75
80
|
|
|
76
|
-
this
|
|
81
|
+
this.___token = store._notificationManager.subscribe(
|
|
77
82
|
parentIdentifier,
|
|
78
83
|
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
79
|
-
if (
|
|
84
|
+
if (bucket === 'relationships' && notifiedKey === key) {
|
|
80
85
|
this._ref++;
|
|
81
86
|
}
|
|
82
87
|
}
|
|
@@ -88,9 +93,11 @@ export default class BelongsToReference {
|
|
|
88
93
|
destroy() {
|
|
89
94
|
// TODO @feature we need the notification manager often enough
|
|
90
95
|
// we should potentially just expose it fully public
|
|
91
|
-
this.store._notificationManager.unsubscribe(this
|
|
92
|
-
|
|
93
|
-
|
|
96
|
+
this.store._notificationManager.unsubscribe(this.___token);
|
|
97
|
+
this.___token = null as unknown as object;
|
|
98
|
+
if (this.___relatedToken) {
|
|
99
|
+
this.store._notificationManager.unsubscribe(this.___relatedToken);
|
|
100
|
+
this.___relatedToken = null;
|
|
94
101
|
}
|
|
95
102
|
}
|
|
96
103
|
|
|
@@ -98,17 +105,18 @@ export default class BelongsToReference {
|
|
|
98
105
|
@dependentKeyCompat
|
|
99
106
|
get _relatedIdentifier(): StableRecordIdentifier | null {
|
|
100
107
|
this._ref; // consume the tracked prop
|
|
101
|
-
if (this
|
|
102
|
-
this.store._notificationManager.unsubscribe(this
|
|
108
|
+
if (this.___relatedToken) {
|
|
109
|
+
this.store._notificationManager.unsubscribe(this.___relatedToken);
|
|
110
|
+
this.___relatedToken = null;
|
|
103
111
|
}
|
|
104
112
|
|
|
105
113
|
let resource = this._resource();
|
|
106
114
|
if (resource && resource.data) {
|
|
107
115
|
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resource.data);
|
|
108
|
-
this
|
|
116
|
+
this.___relatedToken = this.store._notificationManager.subscribe(
|
|
109
117
|
identifier,
|
|
110
118
|
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
111
|
-
if (bucket === 'identity' || (
|
|
119
|
+
if (bucket === 'identity' || (bucket === 'attributes' && notifiedKey === 'id')) {
|
|
112
120
|
this._ref++;
|
|
113
121
|
}
|
|
114
122
|
}
|
|
@@ -125,7 +133,7 @@ export default class BelongsToReference {
|
|
|
125
133
|
`type()` and `id()` methods form a composite key for the identity
|
|
126
134
|
map. This can be used to access the id of an async relationship
|
|
127
135
|
without triggering a fetch that would normally happen if you
|
|
128
|
-
attempted to use `record.
|
|
136
|
+
attempted to use `record.relationship.id`.
|
|
129
137
|
|
|
130
138
|
Example
|
|
131
139
|
|
|
@@ -134,7 +142,7 @@ export default class BelongsToReference {
|
|
|
134
142
|
import Model, { belongsTo } from '@ember-data/model';
|
|
135
143
|
|
|
136
144
|
export default class BlogModel extends Model {
|
|
137
|
-
@belongsTo({ async: true }) user;
|
|
145
|
+
@belongsTo('user', { async: true, inverse: null }) user;
|
|
138
146
|
}
|
|
139
147
|
|
|
140
148
|
let blog = store.push({
|
|
@@ -174,7 +182,7 @@ export default class BelongsToReference {
|
|
|
174
182
|
// models/blog.js
|
|
175
183
|
import Model, { belongsTo } from '@ember-data/model';
|
|
176
184
|
export default Model.extend({
|
|
177
|
-
user: belongsTo({ async: true })
|
|
185
|
+
user: belongsTo('user', { async: true, inverse: null })
|
|
178
186
|
});
|
|
179
187
|
|
|
180
188
|
let blog = store.push({
|
|
@@ -236,7 +244,7 @@ export default class BelongsToReference {
|
|
|
236
244
|
// models/blog.js
|
|
237
245
|
import Model, { belongsTo } from '@ember-data/model';
|
|
238
246
|
export default Model.extend({
|
|
239
|
-
user: belongsTo({ async: true })
|
|
247
|
+
user: belongsTo('user', { async: true, inverse: null })
|
|
240
248
|
});
|
|
241
249
|
|
|
242
250
|
let blog = store.push({
|
|
@@ -277,7 +285,9 @@ export default class BelongsToReference {
|
|
|
277
285
|
}
|
|
278
286
|
|
|
279
287
|
_resource() {
|
|
280
|
-
return this.store._instanceCache
|
|
288
|
+
return this.store._instanceCache
|
|
289
|
+
.getRecordData(this.___identifier)
|
|
290
|
+
.getRelationship(this.___identifier, this.key) as SingleResourceRelationship;
|
|
281
291
|
}
|
|
282
292
|
|
|
283
293
|
/**
|
|
@@ -291,7 +301,7 @@ export default class BelongsToReference {
|
|
|
291
301
|
import Model, { hasMany } from '@ember-data/model';
|
|
292
302
|
|
|
293
303
|
export default class PostModel extends Model {
|
|
294
|
-
@hasMany({ async: true }) comments;
|
|
304
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
295
305
|
}
|
|
296
306
|
```
|
|
297
307
|
|
|
@@ -341,7 +351,7 @@ export default class BelongsToReference {
|
|
|
341
351
|
import Model, { belongsTo } from '@ember-data/model';
|
|
342
352
|
|
|
343
353
|
export default class BlogModel extends Model {
|
|
344
|
-
@belongsTo({ async: true }) user;
|
|
354
|
+
@belongsTo('user', { async: true, inverse: null }) user;
|
|
345
355
|
}
|
|
346
356
|
|
|
347
357
|
let blog = store.push({
|
|
@@ -377,8 +387,25 @@ export default class BelongsToReference {
|
|
|
377
387
|
@return {Promise<record>} A promise that resolves with the new value in this belongs-to relationship.
|
|
378
388
|
*/
|
|
379
389
|
async push(data: SingleResourceDocument | Promise<SingleResourceDocument>): Promise<RecordInstance> {
|
|
380
|
-
|
|
381
|
-
|
|
390
|
+
let jsonApiDoc: SingleResourceDocument = data as SingleResourceDocument;
|
|
391
|
+
if (DEPRECATE_PROMISE_PROXIES && (data as { then: unknown }).then) {
|
|
392
|
+
jsonApiDoc = await resolve(data);
|
|
393
|
+
if (jsonApiDoc !== data) {
|
|
394
|
+
deprecate(
|
|
395
|
+
`You passed in a Promise to a Reference API that now expects a resolved value. await the value before setting it.`,
|
|
396
|
+
false,
|
|
397
|
+
{
|
|
398
|
+
id: 'ember-data:deprecate-promise-proxies',
|
|
399
|
+
until: '5.0',
|
|
400
|
+
since: {
|
|
401
|
+
enabled: '4.8',
|
|
402
|
+
available: '4.8',
|
|
403
|
+
},
|
|
404
|
+
for: 'ember-data',
|
|
405
|
+
}
|
|
406
|
+
);
|
|
407
|
+
}
|
|
408
|
+
}
|
|
382
409
|
let record = this.store.push(jsonApiDoc);
|
|
383
410
|
|
|
384
411
|
// eslint-disable-next-line @typescript-eslint/no-unsafe-call
|
|
@@ -389,9 +416,9 @@ export default class BelongsToReference {
|
|
|
389
416
|
this.store
|
|
390
417
|
);
|
|
391
418
|
|
|
392
|
-
const {
|
|
393
|
-
this.store.
|
|
394
|
-
graph.push({
|
|
419
|
+
const { identifier } = this.belongsToRelationship;
|
|
420
|
+
this.store._join(() => {
|
|
421
|
+
this.graph.push({
|
|
395
422
|
op: 'replaceRelatedRecord',
|
|
396
423
|
record: identifier,
|
|
397
424
|
field: this.key,
|
|
@@ -404,7 +431,7 @@ export default class BelongsToReference {
|
|
|
404
431
|
|
|
405
432
|
/**
|
|
406
433
|
`value()` synchronously returns the current value of the belongs-to
|
|
407
|
-
relationship. Unlike `record.
|
|
434
|
+
relationship. Unlike `record.relationshipName`, calling
|
|
408
435
|
`value()` on a reference does not trigger a fetch if the async
|
|
409
436
|
relationship is not yet loaded. If the relationship is not loaded
|
|
410
437
|
it will always return `null`.
|
|
@@ -416,7 +443,7 @@ export default class BelongsToReference {
|
|
|
416
443
|
import Model, { belongsTo } from '@ember-data/model';
|
|
417
444
|
|
|
418
445
|
export default class BlogModel extends Model {
|
|
419
|
-
@belongsTo({ async: true }) user;
|
|
446
|
+
@belongsTo('user', { async: true, inverse: null }) user;
|
|
420
447
|
}
|
|
421
448
|
|
|
422
449
|
let blog = store.push({
|
|
@@ -469,7 +496,7 @@ export default class BelongsToReference {
|
|
|
469
496
|
import Model, { belongsTo } from '@ember-data/model';
|
|
470
497
|
|
|
471
498
|
export default class BlogModel extends Model {
|
|
472
|
-
@belongsTo({ async: true }) user;
|
|
499
|
+
@belongsTo('user', { async: true, inverse: null }) user;
|
|
473
500
|
}
|
|
474
501
|
|
|
475
502
|
let blog = store.push({
|
|
@@ -520,9 +547,9 @@ export default class BelongsToReference {
|
|
|
520
547
|
@return {Promise} a promise that resolves with the record in this belongs-to relationship.
|
|
521
548
|
*/
|
|
522
549
|
load(options?: Dict<unknown>) {
|
|
523
|
-
const support: LegacySupport = (
|
|
524
|
-
|
|
525
|
-
)
|
|
550
|
+
const support: LegacySupport = (LEGACY_SUPPORT as Map<StableRecordIdentifier, LegacySupport>).get(
|
|
551
|
+
this.___identifier
|
|
552
|
+
)!;
|
|
526
553
|
return support.getBelongsTo(this.key, options);
|
|
527
554
|
}
|
|
528
555
|
|
|
@@ -539,7 +566,7 @@ export default class BelongsToReference {
|
|
|
539
566
|
import Model, { belongsTo } from '@ember-data/model';
|
|
540
567
|
|
|
541
568
|
export default class BlogModel extends Model {
|
|
542
|
-
@belongsTo({ async: true }) user;
|
|
569
|
+
@belongsTo('user', { async: true, inverse: null }) user;
|
|
543
570
|
}
|
|
544
571
|
|
|
545
572
|
let blog = store.push({
|
|
@@ -577,9 +604,9 @@ export default class BelongsToReference {
|
|
|
577
604
|
@return {Promise} a promise that resolves with the record in this belongs-to relationship after the reload has completed.
|
|
578
605
|
*/
|
|
579
606
|
reload(options?: Dict<unknown>) {
|
|
580
|
-
const support: LegacySupport = (
|
|
581
|
-
|
|
582
|
-
)
|
|
607
|
+
const support: LegacySupport = (LEGACY_SUPPORT as Map<StableRecordIdentifier, LegacySupport>).get(
|
|
608
|
+
this.___identifier
|
|
609
|
+
)!;
|
|
583
610
|
return support.reloadBelongsTo(this.key, options).then(() => this.value());
|
|
584
611
|
}
|
|
585
612
|
}
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { deprecate } from '@ember/debug';
|
|
1
2
|
import { dependentKeyCompat } from '@ember/object/compat';
|
|
2
3
|
import { DEBUG } from '@glimmer/env';
|
|
3
4
|
import { cached, tracked } from '@glimmer/tracking';
|
|
@@ -7,12 +8,13 @@ import { resolve } from 'rsvp';
|
|
|
7
8
|
|
|
8
9
|
import { ManyArray } from 'ember-data/-private';
|
|
9
10
|
|
|
10
|
-
import
|
|
11
|
+
import { DEPRECATE_PROMISE_PROXIES } from '@ember-data/private-build-infra/deprecations';
|
|
12
|
+
import type { Graph } from '@ember-data/record-data/-private/graph/graph';
|
|
13
|
+
import type ManyRelationship from '@ember-data/record-data/-private/relationships/state/has-many';
|
|
11
14
|
import type Store from '@ember-data/store';
|
|
12
15
|
import { recordIdentifierFor } from '@ember-data/store';
|
|
13
16
|
import { assertPolymorphicType } from '@ember-data/store/-debug';
|
|
14
|
-
import type { NotificationType } from '@ember-data/store/-private/record-notification-manager';
|
|
15
|
-
import type { DebugWeakCache } from '@ember-data/store/-private/weak-cache';
|
|
17
|
+
import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
|
|
16
18
|
import type {
|
|
17
19
|
CollectionResourceDocument,
|
|
18
20
|
CollectionResourceRelationship,
|
|
@@ -53,48 +55,51 @@ function isResourceIdentiferWithRelatedLinks(
|
|
|
53
55
|
@extends Reference
|
|
54
56
|
*/
|
|
55
57
|
export default class HasManyReference {
|
|
58
|
+
declare graph: Graph;
|
|
56
59
|
declare key: string;
|
|
57
60
|
declare hasManyRelationship: ManyRelationship;
|
|
58
61
|
declare type: string;
|
|
59
62
|
declare store: Store;
|
|
60
63
|
|
|
61
64
|
// unsubscribe tokens given to us by the notification manager
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
+
___token!: Object;
|
|
66
|
+
___identifier: StableRecordIdentifier;
|
|
67
|
+
___relatedTokenMap!: Map<StableRecordIdentifier, Object>;
|
|
65
68
|
|
|
66
69
|
@tracked _ref = 0;
|
|
67
70
|
|
|
68
71
|
constructor(
|
|
69
72
|
store: Store,
|
|
73
|
+
graph: Graph,
|
|
70
74
|
parentIdentifier: StableRecordIdentifier,
|
|
71
75
|
hasManyRelationship: ManyRelationship,
|
|
72
76
|
key: string
|
|
73
77
|
) {
|
|
78
|
+
this.graph = graph;
|
|
74
79
|
this.key = key;
|
|
75
80
|
this.hasManyRelationship = hasManyRelationship;
|
|
76
81
|
this.type = hasManyRelationship.definition.type;
|
|
77
82
|
|
|
78
83
|
this.store = store;
|
|
79
|
-
this
|
|
80
|
-
this
|
|
84
|
+
this.___identifier = parentIdentifier;
|
|
85
|
+
this.___token = store._notificationManager.subscribe(
|
|
81
86
|
parentIdentifier,
|
|
82
87
|
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
83
|
-
if (
|
|
88
|
+
if (bucket === 'relationships' && notifiedKey === key) {
|
|
84
89
|
this._ref++;
|
|
85
90
|
}
|
|
86
91
|
}
|
|
87
92
|
);
|
|
88
|
-
this
|
|
93
|
+
this.___relatedTokenMap = new Map();
|
|
89
94
|
// TODO inverse
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
destroy() {
|
|
93
|
-
this.store._notificationManager.unsubscribe(this
|
|
94
|
-
this
|
|
98
|
+
this.store._notificationManager.unsubscribe(this.___token);
|
|
99
|
+
this.___relatedTokenMap.forEach((token) => {
|
|
95
100
|
this.store._notificationManager.unsubscribe(token);
|
|
96
101
|
});
|
|
97
|
-
this
|
|
102
|
+
this.___relatedTokenMap.clear();
|
|
98
103
|
}
|
|
99
104
|
|
|
100
105
|
@cached
|
|
@@ -104,34 +109,44 @@ export default class HasManyReference {
|
|
|
104
109
|
|
|
105
110
|
let resource = this._resource();
|
|
106
111
|
|
|
107
|
-
this
|
|
108
|
-
|
|
109
|
-
});
|
|
110
|
-
this.#relatedTokenMap.clear();
|
|
112
|
+
let map = this.___relatedTokenMap;
|
|
113
|
+
this.___relatedTokenMap = new Map();
|
|
111
114
|
|
|
112
115
|
if (resource && resource.data) {
|
|
113
116
|
return resource.data.map((resourceIdentifier) => {
|
|
114
117
|
const identifier = this.store.identifierCache.getOrCreateRecordIdentifier(resourceIdentifier);
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
118
|
+
let token = map.get(identifier);
|
|
119
|
+
|
|
120
|
+
if (token) {
|
|
121
|
+
map.delete(identifier);
|
|
122
|
+
} else {
|
|
123
|
+
token = this.store._notificationManager.subscribe(
|
|
124
|
+
identifier,
|
|
125
|
+
(_: StableRecordIdentifier, bucket: NotificationType, notifiedKey?: string) => {
|
|
126
|
+
if (bucket === 'identity' || (bucket === 'attributes' && notifiedKey === 'id')) {
|
|
127
|
+
this._ref++;
|
|
128
|
+
}
|
|
120
129
|
}
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
this.#relatedTokenMap.set(identifier, token);
|
|
130
|
+
);
|
|
131
|
+
}
|
|
132
|
+
this.___relatedTokenMap.set(identifier, token);
|
|
125
133
|
|
|
126
134
|
return identifier;
|
|
127
135
|
});
|
|
128
136
|
}
|
|
129
137
|
|
|
138
|
+
map.forEach((token) => {
|
|
139
|
+
this.store._notificationManager.unsubscribe(token);
|
|
140
|
+
});
|
|
141
|
+
map.clear();
|
|
142
|
+
|
|
130
143
|
return [];
|
|
131
144
|
}
|
|
132
145
|
|
|
133
146
|
_resource() {
|
|
134
|
-
return this.store._instanceCache
|
|
147
|
+
return this.store._instanceCache
|
|
148
|
+
.getRecordData(this.___identifier)
|
|
149
|
+
.getRelationship(this.___identifier, this.key) as CollectionResourceRelationship;
|
|
135
150
|
}
|
|
136
151
|
|
|
137
152
|
/**
|
|
@@ -145,7 +160,7 @@ export default class HasManyReference {
|
|
|
145
160
|
import Model, { hasMany } from '@ember-data/model';
|
|
146
161
|
|
|
147
162
|
export default class PostModel extends Model {
|
|
148
|
-
@hasMany({ async: true }) comments;
|
|
163
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
149
164
|
}
|
|
150
165
|
```
|
|
151
166
|
|
|
@@ -194,7 +209,7 @@ export default class HasManyReference {
|
|
|
194
209
|
import Model, { hasMany } from '@ember-data/model';
|
|
195
210
|
|
|
196
211
|
export default class PostModel extends Model {
|
|
197
|
-
@hasMany({ async: true }) comments;
|
|
212
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
198
213
|
}
|
|
199
214
|
```
|
|
200
215
|
|
|
@@ -234,7 +249,7 @@ export default class HasManyReference {
|
|
|
234
249
|
// models/blog.js
|
|
235
250
|
import Model, { belongsTo } from '@ember-data/model';
|
|
236
251
|
export default Model.extend({
|
|
237
|
-
user: belongsTo({ async: true })
|
|
252
|
+
user: belongsTo('user', { async: true, inverse: null })
|
|
238
253
|
});
|
|
239
254
|
|
|
240
255
|
let blog = store.push({
|
|
@@ -296,7 +311,7 @@ export default class HasManyReference {
|
|
|
296
311
|
// models/blog.js
|
|
297
312
|
import Model, { hasMany } from '@ember-data/model';
|
|
298
313
|
export default Model.extend({
|
|
299
|
-
users: hasMany({ async: true })
|
|
314
|
+
users: hasMany('user', { async: true, inverse: null })
|
|
300
315
|
});
|
|
301
316
|
|
|
302
317
|
let blog = store.push({
|
|
@@ -347,7 +362,7 @@ export default class HasManyReference {
|
|
|
347
362
|
import Model, { hasMany } from '@ember-data/model';
|
|
348
363
|
|
|
349
364
|
export default class PostModel extends Model {
|
|
350
|
-
@hasMany({ async: true }) comments;
|
|
365
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
351
366
|
}
|
|
352
367
|
```
|
|
353
368
|
|
|
@@ -384,7 +399,25 @@ export default class HasManyReference {
|
|
|
384
399
|
async push(
|
|
385
400
|
objectOrPromise: ExistingResourceObject[] | CollectionResourceDocument | { data: SingleResourceDocument[] }
|
|
386
401
|
): Promise<ManyArray> {
|
|
387
|
-
|
|
402
|
+
let payload = objectOrPromise;
|
|
403
|
+
if (DEPRECATE_PROMISE_PROXIES && (objectOrPromise as unknown as { then: unknown }).then) {
|
|
404
|
+
payload = await resolve(objectOrPromise);
|
|
405
|
+
if (payload !== objectOrPromise) {
|
|
406
|
+
deprecate(
|
|
407
|
+
`You passed in a Promise to a Reference API that now expects a resolved value. await the value before setting it.`,
|
|
408
|
+
false,
|
|
409
|
+
{
|
|
410
|
+
id: 'ember-data:deprecate-promise-proxies',
|
|
411
|
+
until: '5.0',
|
|
412
|
+
since: {
|
|
413
|
+
enabled: '4.8',
|
|
414
|
+
available: '4.8',
|
|
415
|
+
},
|
|
416
|
+
for: 'ember-data',
|
|
417
|
+
}
|
|
418
|
+
);
|
|
419
|
+
}
|
|
420
|
+
}
|
|
388
421
|
let array: Array<ExistingResourceObject | SingleResourceDocument>;
|
|
389
422
|
|
|
390
423
|
if (!Array.isArray(payload) && typeof payload === 'object' && Array.isArray(payload.data)) {
|
|
@@ -414,9 +447,9 @@ export default class HasManyReference {
|
|
|
414
447
|
return recordIdentifierFor(record);
|
|
415
448
|
});
|
|
416
449
|
|
|
417
|
-
const {
|
|
418
|
-
store.
|
|
419
|
-
graph.push({
|
|
450
|
+
const { identifier } = this.hasManyRelationship;
|
|
451
|
+
store._join(() => {
|
|
452
|
+
this.graph.push({
|
|
420
453
|
op: 'replaceRelatedRecords',
|
|
421
454
|
record: identifier,
|
|
422
455
|
field: this.key,
|
|
@@ -433,18 +466,16 @@ export default class HasManyReference {
|
|
|
433
466
|
return false;
|
|
434
467
|
}
|
|
435
468
|
|
|
436
|
-
let
|
|
469
|
+
let localState = this.hasManyRelationship.localState;
|
|
437
470
|
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
let internalModel = this.store._instanceCache._internalModelForResource(identifier);
|
|
441
|
-
return internalModel.isLoaded === true;
|
|
471
|
+
return localState.every((identifier) => {
|
|
472
|
+
return this.store._instanceCache.recordIsLoaded(identifier, true) === true;
|
|
442
473
|
});
|
|
443
474
|
}
|
|
444
475
|
|
|
445
476
|
/**
|
|
446
477
|
`value()` synchronously returns the current value of the has-many
|
|
447
|
-
relationship. Unlike `record.
|
|
478
|
+
relationship. Unlike `record.relationshipName`, calling
|
|
448
479
|
`value()` on a reference does not trigger a fetch if the async
|
|
449
480
|
relationship is not yet loaded. If the relationship is not loaded
|
|
450
481
|
it will always return `null`.
|
|
@@ -455,7 +486,7 @@ export default class HasManyReference {
|
|
|
455
486
|
import Model, { hasMany } from '@ember-data/model';
|
|
456
487
|
|
|
457
488
|
export default class PostModel extends Model {
|
|
458
|
-
@hasMany({ async: true }) comments;
|
|
489
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
459
490
|
}
|
|
460
491
|
```
|
|
461
492
|
|
|
@@ -474,7 +505,7 @@ export default class HasManyReference {
|
|
|
474
505
|
|
|
475
506
|
let commentsRef = post.hasMany('comments');
|
|
476
507
|
|
|
477
|
-
post.
|
|
508
|
+
post.comments.then(function(comments) {
|
|
478
509
|
commentsRef.value() === comments
|
|
479
510
|
})
|
|
480
511
|
```
|
|
@@ -484,9 +515,9 @@ export default class HasManyReference {
|
|
|
484
515
|
@return {ManyArray}
|
|
485
516
|
*/
|
|
486
517
|
value() {
|
|
487
|
-
const support: LegacySupport = (
|
|
488
|
-
|
|
489
|
-
)
|
|
518
|
+
const support: LegacySupport = (LEGACY_SUPPORT as Map<StableRecordIdentifier, LegacySupport>).get(
|
|
519
|
+
this.___identifier
|
|
520
|
+
)!;
|
|
490
521
|
|
|
491
522
|
return this._isLoaded() ? support.getManyArray(this.key) : null;
|
|
492
523
|
}
|
|
@@ -503,7 +534,7 @@ export default class HasManyReference {
|
|
|
503
534
|
import Model, { hasMany } from '@ember-data/model';
|
|
504
535
|
|
|
505
536
|
export default class PostModel extends Model {
|
|
506
|
-
@hasMany({ async: true }) comments;
|
|
537
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
507
538
|
}
|
|
508
539
|
```
|
|
509
540
|
|
|
@@ -556,9 +587,9 @@ export default class HasManyReference {
|
|
|
556
587
|
this has-many relationship.
|
|
557
588
|
*/
|
|
558
589
|
async load(options?: FindOptions): Promise<ManyArray> {
|
|
559
|
-
const support: LegacySupport = (
|
|
560
|
-
|
|
561
|
-
)
|
|
590
|
+
const support: LegacySupport = (LEGACY_SUPPORT as Map<StableRecordIdentifier, LegacySupport>).get(
|
|
591
|
+
this.___identifier
|
|
592
|
+
)!;
|
|
562
593
|
return support.getHasMany(this.key, options) as Promise<ManyArray> | ManyArray; // this cast is necessary because typescript does not work properly with custom thenables;
|
|
563
594
|
}
|
|
564
595
|
|
|
@@ -572,7 +603,7 @@ export default class HasManyReference {
|
|
|
572
603
|
import Model, { hasMany } from '@ember-data/model';
|
|
573
604
|
|
|
574
605
|
export default class PostModel extends Model {
|
|
575
|
-
@hasMany({ async: true }) comments;
|
|
606
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
576
607
|
}
|
|
577
608
|
```
|
|
578
609
|
|
|
@@ -613,9 +644,9 @@ export default class HasManyReference {
|
|
|
613
644
|
@return {Promise} a promise that resolves with the ManyArray in this has-many relationship.
|
|
614
645
|
*/
|
|
615
646
|
reload(options?: FindOptions) {
|
|
616
|
-
const support: LegacySupport = (
|
|
617
|
-
|
|
618
|
-
)
|
|
647
|
+
const support: LegacySupport = (LEGACY_SUPPORT as Map<StableRecordIdentifier, LegacySupport>).get(
|
|
648
|
+
this.___identifier
|
|
649
|
+
)!;
|
|
619
650
|
return support.reloadHasMany(this.key, options);
|
|
620
651
|
}
|
|
621
652
|
}
|
|
@@ -1,17 +1,13 @@
|
|
|
1
|
+
import { dasherize } from '@ember/string';
|
|
1
2
|
import { DEBUG } from '@glimmer/env';
|
|
2
3
|
|
|
3
4
|
import { singularize } from 'ember-inflector';
|
|
4
5
|
|
|
5
6
|
import type Store from '@ember-data/store';
|
|
6
|
-
import { normalizeModelName } from '@ember-data/store/-private';
|
|
7
7
|
import type { RelationshipSchema } from '@ember-data/types/q/record-data-schemas';
|
|
8
8
|
|
|
9
|
-
/**
|
|
10
|
-
@module @ember-data/store
|
|
11
|
-
*/
|
|
12
|
-
|
|
13
9
|
function typeForRelationshipMeta(meta) {
|
|
14
|
-
let modelName =
|
|
10
|
+
let modelName = dasherize(meta.type || meta.key);
|
|
15
11
|
|
|
16
12
|
if (meta.kind === 'hasMany') {
|
|
17
13
|
modelName = singularize(modelName);
|
|
@@ -25,10 +21,9 @@ function shouldFindInverse(relationshipMeta) {
|
|
|
25
21
|
return !(options && options.inverse === null);
|
|
26
22
|
}
|
|
27
23
|
|
|
28
|
-
|
|
24
|
+
class RelationshipDefinition implements RelationshipSchema {
|
|
29
25
|
declare _type: string;
|
|
30
26
|
declare __inverseKey: string;
|
|
31
|
-
declare __inverseIsAsync: boolean;
|
|
32
27
|
declare __hasCalculatedInverse: boolean;
|
|
33
28
|
declare parentModelName: string;
|
|
34
29
|
declare inverseIsAsync: string | null;
|
|
@@ -37,7 +32,6 @@ export class RelationshipDefinition implements RelationshipSchema {
|
|
|
37
32
|
constructor(meta: any) {
|
|
38
33
|
this._type = '';
|
|
39
34
|
this.__inverseKey = '';
|
|
40
|
-
this.__inverseIsAsync = true;
|
|
41
35
|
this.__hasCalculatedInverse = false;
|
|
42
36
|
this.parentModelName = meta.parentModelName;
|
|
43
37
|
this.meta = meta;
|
|
@@ -74,40 +68,28 @@ export class RelationshipDefinition implements RelationshipSchema {
|
|
|
74
68
|
return this.__inverseKey;
|
|
75
69
|
}
|
|
76
70
|
|
|
77
|
-
_inverseIsAsync(store: Store, modelClass): boolean {
|
|
78
|
-
if (this.__hasCalculatedInverse === false) {
|
|
79
|
-
this._calculateInverse(store, modelClass);
|
|
80
|
-
}
|
|
81
|
-
return this.__inverseIsAsync;
|
|
82
|
-
}
|
|
83
|
-
|
|
84
71
|
_calculateInverse(store: Store, modelClass): void {
|
|
85
72
|
this.__hasCalculatedInverse = true;
|
|
86
|
-
let inverseKey
|
|
73
|
+
let inverseKey;
|
|
87
74
|
let inverse: any = null;
|
|
88
75
|
|
|
89
76
|
if (shouldFindInverse(this.meta)) {
|
|
90
77
|
inverse = modelClass.inverseFor(this.key, store);
|
|
91
|
-
}
|
|
78
|
+
}
|
|
79
|
+
// TODO make this error again for the non-polymorphic case
|
|
80
|
+
if (DEBUG && !this.options.polymorphic) {
|
|
92
81
|
modelClass.typeForRelationship(this.key, store);
|
|
93
82
|
}
|
|
94
83
|
|
|
95
84
|
if (inverse) {
|
|
96
85
|
inverseKey = inverse.name;
|
|
97
|
-
inverseIsAsync = isRelationshipAsync(inverse);
|
|
98
86
|
} else {
|
|
99
87
|
inverseKey = null;
|
|
100
|
-
inverseIsAsync = false;
|
|
101
88
|
}
|
|
102
89
|
this.__inverseKey = inverseKey;
|
|
103
|
-
this.__inverseIsAsync = inverseIsAsync;
|
|
104
90
|
}
|
|
105
91
|
}
|
|
106
|
-
|
|
107
|
-
function isRelationshipAsync(meta: RelationshipSchema): boolean {
|
|
108
|
-
let inverseAsync = meta.options && meta.options.async;
|
|
109
|
-
return typeof inverseAsync === 'undefined' ? true : inverseAsync;
|
|
110
|
-
}
|
|
92
|
+
export type { RelationshipDefinition };
|
|
111
93
|
|
|
112
94
|
export function relationshipFromMeta(meta: RelationshipSchema): RelationshipDefinition {
|
|
113
95
|
return new RelationshipDefinition(meta);
|
package/index.js
CHANGED
|
@@ -17,8 +17,11 @@ module.exports = Object.assign({}, addonBaseConfig, {
|
|
|
17
17
|
'@ember-data/store/-private',
|
|
18
18
|
'@ember-data/store/-debug',
|
|
19
19
|
'@embroider/macros',
|
|
20
|
+
'@ember/string',
|
|
20
21
|
'@embroider/macros/es-compat',
|
|
21
22
|
|
|
23
|
+
'@ember/object/proxy',
|
|
24
|
+
'@ember/object/promise-proxy-mixin',
|
|
22
25
|
'@ember/application',
|
|
23
26
|
'@ember/array',
|
|
24
27
|
'@ember/array/mutable',
|