@ember-data/model 4.8.0-alpha.3 → 4.8.0-alpha.6
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 +5 -31
- package/addon/-private/belongs-to.js +78 -22
- package/addon/-private/deprecated-promise-proxy.ts +54 -0
- package/addon/-private/has-many.js +77 -19
- package/addon/-private/legacy-data-fetch.js +28 -8
- package/addon/-private/legacy-relationships-support.ts +247 -180
- package/addon/-private/many-array.ts +213 -245
- package/addon/-private/model.js +53 -38
- package/addon/-private/notify-changes.ts +1 -1
- package/addon/-private/promise-belongs-to.ts +1 -1
- package/addon/-private/promise-many-array.ts +35 -13
- package/addon/-private/promise-proxy-base.js +4 -0
- package/addon/-private/record-state.ts +7 -13
- package/addon/-private/references/belongs-to.ts +62 -35
- package/addon/-private/references/has-many.ts +82 -49
- package/addon/-private/relationship-meta.ts +3 -23
- package/index.js +2 -0
- package/package.json +12 -8
package/addon/-private/model.js
CHANGED
|
@@ -7,7 +7,6 @@ import EmberError from '@ember/error';
|
|
|
7
7
|
import EmberObject from '@ember/object';
|
|
8
8
|
import { dependentKeyCompat } from '@ember/object/compat';
|
|
9
9
|
import { run } from '@ember/runloop';
|
|
10
|
-
import { inject as service } from '@ember/service';
|
|
11
10
|
import { isNone } from '@ember/utils';
|
|
12
11
|
import { DEBUG } from '@glimmer/env';
|
|
13
12
|
import { tracked } from '@glimmer/tracking';
|
|
@@ -19,11 +18,13 @@ import { HAS_DEBUG_PACKAGE } from '@ember-data/private-build-infra';
|
|
|
19
18
|
import {
|
|
20
19
|
DEPRECATE_EARLY_STATIC,
|
|
21
20
|
DEPRECATE_MODEL_REOPEN,
|
|
21
|
+
DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE,
|
|
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,
|
|
25
|
+
import { coerceId, recordDataFor } from '@ember-data/store/-private';
|
|
26
26
|
|
|
27
|
+
import { deprecatedPromiseObject } from './deprecated-promise-proxy';
|
|
27
28
|
import Errors from './errors';
|
|
28
29
|
import { LegacySupport } from './legacy-relationships-support';
|
|
29
30
|
import notifyChanges from './notify-changes';
|
|
@@ -31,18 +32,21 @@ import RecordState, { peekTag, tagged } from './record-state';
|
|
|
31
32
|
import { relationshipFromMeta } from './relationship-meta';
|
|
32
33
|
|
|
33
34
|
const { changeProperties } = Ember;
|
|
34
|
-
export const LEGACY_SUPPORT = new
|
|
35
|
-
|
|
35
|
+
export const LEGACY_SUPPORT = new Map();
|
|
36
|
+
|
|
37
|
+
export function lookupLegacySupport(record) {
|
|
36
38
|
const identifier = recordIdentifierFor(record);
|
|
37
39
|
let support = LEGACY_SUPPORT.get(identifier);
|
|
38
40
|
|
|
39
41
|
if (!support) {
|
|
40
42
|
support = new LegacySupport(record);
|
|
41
43
|
LEGACY_SUPPORT.set(identifier, support);
|
|
44
|
+
LEGACY_SUPPORT.set(record, support);
|
|
42
45
|
}
|
|
43
46
|
|
|
44
47
|
return support;
|
|
45
|
-
}
|
|
48
|
+
}
|
|
49
|
+
|
|
46
50
|
function findPossibleInverses(type, inverseType, name, relationshipsSoFar) {
|
|
47
51
|
let possibleRelationships = relationshipsSoFar || [];
|
|
48
52
|
|
|
@@ -118,8 +122,7 @@ function computeOnce(target, key, desc) {
|
|
|
118
122
|
@extends Ember.EmberObject
|
|
119
123
|
*/
|
|
120
124
|
class Model extends EmberObject {
|
|
121
|
-
|
|
122
|
-
#notifications;
|
|
125
|
+
___private_notifications;
|
|
123
126
|
|
|
124
127
|
init(options = {}) {
|
|
125
128
|
if (DEBUG && !options._secretInit && !options._createProps) {
|
|
@@ -129,37 +132,46 @@ class Model extends EmberObject {
|
|
|
129
132
|
}
|
|
130
133
|
const createProps = options._createProps;
|
|
131
134
|
const _secretInit = options._secretInit;
|
|
132
|
-
|
|
133
|
-
|
|
135
|
+
options._createProps = null;
|
|
136
|
+
options._secretInit = null;
|
|
137
|
+
|
|
138
|
+
let store = (this.store = _secretInit.store);
|
|
134
139
|
super.init(options);
|
|
135
140
|
|
|
136
|
-
_secretInit
|
|
141
|
+
let identity = _secretInit.identifier;
|
|
142
|
+
_secretInit.cb(this, _secretInit.recordData, identity, _secretInit.store);
|
|
143
|
+
|
|
137
144
|
this.___recordState = DEBUG ? new RecordState(this) : null;
|
|
138
145
|
|
|
139
146
|
this.setProperties(createProps);
|
|
140
147
|
|
|
141
|
-
let store = storeFor(this);
|
|
142
148
|
let notifications = store._notificationManager;
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
this.#notifications = notifications.subscribe(identity, (identifier, type, key) => {
|
|
149
|
+
this.___private_notifications = notifications.subscribe(identity, (identifier, type, key) => {
|
|
146
150
|
notifyChanges(identifier, type, key, this, store);
|
|
147
151
|
});
|
|
148
152
|
}
|
|
149
153
|
|
|
150
154
|
destroy() {
|
|
151
|
-
|
|
155
|
+
const identifier = recordIdentifierFor(this);
|
|
152
156
|
this.___recordState?.destroy();
|
|
153
157
|
const store = storeFor(this);
|
|
154
|
-
|
|
155
|
-
store._notificationManager.unsubscribe(this.#notifications);
|
|
158
|
+
store._notificationManager.unsubscribe(this.___private_notifications);
|
|
156
159
|
// Legacy behavior is to notify the relationships on destroy
|
|
157
160
|
// such that they "clear". It's uncertain this behavior would
|
|
158
161
|
// be good for a new model paradigm, likely cheaper and safer
|
|
159
162
|
// to simply not notify, for this reason the store does not itself
|
|
160
163
|
// notify individual changes once the delete has been signaled,
|
|
161
164
|
// this decision is left to model instances.
|
|
162
|
-
|
|
165
|
+
|
|
166
|
+
this.eachRelationship((key, meta) => {
|
|
167
|
+
if (meta.kind === 'belongsTo') {
|
|
168
|
+
this.notifyPropertyChange(key);
|
|
169
|
+
}
|
|
170
|
+
});
|
|
171
|
+
LEGACY_SUPPORT.get(this)?.destroy();
|
|
172
|
+
LEGACY_SUPPORT.delete(this);
|
|
173
|
+
LEGACY_SUPPORT.delete(identifier);
|
|
174
|
+
|
|
163
175
|
super.destroy();
|
|
164
176
|
}
|
|
165
177
|
|
|
@@ -487,14 +499,13 @@ class Model extends EmberObject {
|
|
|
487
499
|
);
|
|
488
500
|
|
|
489
501
|
if (normalizedId !== null && didChange) {
|
|
490
|
-
this.store._instanceCache.setRecordId(identifier
|
|
502
|
+
this.store._instanceCache.setRecordId(identifier, normalizedId);
|
|
491
503
|
this.store._notificationManager.notify(identifier, 'identity');
|
|
492
504
|
}
|
|
493
505
|
}
|
|
494
506
|
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
return this.id;
|
|
507
|
+
toString() {
|
|
508
|
+
return `<model::${this.constructor.modelName}:${this.id}>`;
|
|
498
509
|
}
|
|
499
510
|
|
|
500
511
|
/**
|
|
@@ -813,7 +824,7 @@ class Model extends EmberObject {
|
|
|
813
824
|
and value is an [oldProp, newProp] array.
|
|
814
825
|
*/
|
|
815
826
|
changedAttributes() {
|
|
816
|
-
return recordDataFor(this).
|
|
827
|
+
return recordDataFor(this).changedAttrs(recordIdentifierFor(this));
|
|
817
828
|
}
|
|
818
829
|
|
|
819
830
|
/**
|
|
@@ -837,12 +848,15 @@ class Model extends EmberObject {
|
|
|
837
848
|
rollbackAttributes() {
|
|
838
849
|
const { currentState } = this;
|
|
839
850
|
const { isNew } = currentState;
|
|
840
|
-
|
|
841
|
-
this.
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
851
|
+
|
|
852
|
+
storeFor(this)._join(() => {
|
|
853
|
+
recordDataFor(this).rollbackAttrs(recordIdentifierFor(this));
|
|
854
|
+
this.errors.clear();
|
|
855
|
+
currentState.cleanErrorRequests();
|
|
856
|
+
if (isNew) {
|
|
857
|
+
this.unloadRecord();
|
|
858
|
+
}
|
|
859
|
+
});
|
|
846
860
|
}
|
|
847
861
|
|
|
848
862
|
/**
|
|
@@ -980,7 +994,7 @@ class Model extends EmberObject {
|
|
|
980
994
|
import Model, { belongsTo } from '@ember-data/model';
|
|
981
995
|
|
|
982
996
|
export default class BlogModel extends Model {
|
|
983
|
-
@belongsTo({ async: true }) user;
|
|
997
|
+
@belongsTo('user', { async: true, inverse: null }) user;
|
|
984
998
|
}
|
|
985
999
|
```
|
|
986
1000
|
|
|
@@ -1036,7 +1050,7 @@ class Model extends EmberObject {
|
|
|
1036
1050
|
@return {BelongsToReference} reference for this relationship
|
|
1037
1051
|
*/
|
|
1038
1052
|
belongsTo(name) {
|
|
1039
|
-
return
|
|
1053
|
+
return lookupLegacySupport(this).referenceFor('belongsTo', name);
|
|
1040
1054
|
}
|
|
1041
1055
|
|
|
1042
1056
|
/**
|
|
@@ -1048,7 +1062,7 @@ class Model extends EmberObject {
|
|
|
1048
1062
|
import Model, { hasMany } from '@ember-data/model';
|
|
1049
1063
|
|
|
1050
1064
|
export default class BlogModel extends Model {
|
|
1051
|
-
@hasMany({ async: true }) comments;
|
|
1065
|
+
@hasMany('comment', { async: true, inverse: null }) comments;
|
|
1052
1066
|
}
|
|
1053
1067
|
|
|
1054
1068
|
let blog = store.push({
|
|
@@ -1099,7 +1113,7 @@ class Model extends EmberObject {
|
|
|
1099
1113
|
@return {HasManyReference} reference for this relationship
|
|
1100
1114
|
*/
|
|
1101
1115
|
hasMany(name) {
|
|
1102
|
-
return
|
|
1116
|
+
return lookupLegacySupport(this).referenceFor('hasMany', name);
|
|
1103
1117
|
}
|
|
1104
1118
|
|
|
1105
1119
|
/**
|
|
@@ -1141,7 +1155,7 @@ class Model extends EmberObject {
|
|
|
1141
1155
|
record.eachRelationship(function(name, descriptor) {
|
|
1142
1156
|
if (descriptor.kind === 'hasMany') {
|
|
1143
1157
|
let serializedHasManyName = name.toUpperCase() + '_IDS';
|
|
1144
|
-
json[serializedHasManyName] = record.get(name).
|
|
1158
|
+
json[serializedHasManyName] = record.get(name).map(r => r.id);
|
|
1145
1159
|
}
|
|
1146
1160
|
});
|
|
1147
1161
|
|
|
@@ -1201,7 +1215,7 @@ class Model extends EmberObject {
|
|
|
1201
1215
|
|
|
1202
1216
|
```javascript
|
|
1203
1217
|
import RESTSerializer from '@ember-data/serializer/rest';
|
|
1204
|
-
import { underscore } from '
|
|
1218
|
+
import { underscore } from '<app-name>/utils/string-utils';
|
|
1205
1219
|
|
|
1206
1220
|
export default const PostSerializer = RESTSerializer.extend({
|
|
1207
1221
|
payloadKeyFromModelName(modelName) {
|
|
@@ -1795,7 +1809,7 @@ class Model extends EmberObject {
|
|
|
1795
1809
|
meta.key = name;
|
|
1796
1810
|
meta.name = name;
|
|
1797
1811
|
meta.parentModelName = modelName;
|
|
1798
|
-
relationships[name] = relationshipFromMeta(meta);
|
|
1812
|
+
relationships[name] = DEPRECATE_RELATIONSHIPS_WITHOUT_INVERSE ? relationshipFromMeta(meta) : meta;
|
|
1799
1813
|
}
|
|
1800
1814
|
});
|
|
1801
1815
|
return relationships;
|
|
@@ -1865,6 +1879,7 @@ class Model extends EmberObject {
|
|
|
1865
1879
|
let map = new Map();
|
|
1866
1880
|
|
|
1867
1881
|
this.eachComputedProperty((name, meta) => {
|
|
1882
|
+
// TODO end reliance on these booleans and stop leaking them in the spec
|
|
1868
1883
|
if (meta.isRelationship) {
|
|
1869
1884
|
map.set(name, meta.kind);
|
|
1870
1885
|
} else if (meta.isAttribute) {
|
|
@@ -2411,7 +2426,7 @@ if (DEBUG) {
|
|
|
2411
2426
|
until: '5.0',
|
|
2412
2427
|
since: { available: '4.8', enabled: '4.8' },
|
|
2413
2428
|
});
|
|
2414
|
-
return originalReopen.call(this, arguments);
|
|
2429
|
+
return originalReopen.call(this, ...arguments);
|
|
2415
2430
|
};
|
|
2416
2431
|
|
|
2417
2432
|
Model.reopenClass = function deprecatedReopenClass() {
|
|
@@ -2425,7 +2440,7 @@ if (DEBUG) {
|
|
|
2425
2440
|
since: { available: '4.8', enabled: '4.8' },
|
|
2426
2441
|
}
|
|
2427
2442
|
);
|
|
2428
|
-
return originalReopenClass.call(this, arguments);
|
|
2443
|
+
return originalReopenClass.call(this, ...arguments);
|
|
2429
2444
|
};
|
|
2430
2445
|
}
|
|
2431
2446
|
}
|
|
@@ -66,7 +66,7 @@ function notifyRelationship(identifier: StableRecordIdentifier, key: string, rec
|
|
|
66
66
|
function notifyAttribute(store: Store, identifier: StableRecordIdentifier, key: string, record: Model) {
|
|
67
67
|
let currentValue = cacheFor(record, key);
|
|
68
68
|
|
|
69
|
-
if (currentValue !== store._instanceCache.getRecordData(identifier).getAttr(key)) {
|
|
69
|
+
if (currentValue !== store._instanceCache.getRecordData(identifier).getAttr(identifier, key)) {
|
|
70
70
|
record.notifyPropertyChange(key);
|
|
71
71
|
}
|
|
72
72
|
}
|
|
@@ -4,11 +4,11 @@ import type PromiseProxyMixin from '@ember/object/promise-proxy-mixin';
|
|
|
4
4
|
import type ObjectProxy from '@ember/object/proxy';
|
|
5
5
|
|
|
6
6
|
import type Store from '@ember-data/store';
|
|
7
|
-
import { PromiseObject } from '@ember-data/store/-private';
|
|
8
7
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
9
8
|
import type { Dict } from '@ember-data/types/q/utils';
|
|
10
9
|
|
|
11
10
|
import { LegacySupport } from './legacy-relationships-support';
|
|
11
|
+
import { PromiseObject } from './promise-proxy-base';
|
|
12
12
|
|
|
13
13
|
export interface BelongsToProxyMeta {
|
|
14
14
|
key: string;
|
|
@@ -2,18 +2,23 @@ import ArrayMixin, { NativeArray } from '@ember/array';
|
|
|
2
2
|
import type ArrayProxy from '@ember/array/proxy';
|
|
3
3
|
import { assert, deprecate } from '@ember/debug';
|
|
4
4
|
import { dependentKeyCompat } from '@ember/object/compat';
|
|
5
|
+
import { DEBUG } from '@glimmer/env';
|
|
5
6
|
import { tracked } from '@glimmer/tracking';
|
|
6
7
|
import Ember from 'ember';
|
|
7
8
|
|
|
8
9
|
import { resolve } from 'rsvp';
|
|
9
10
|
|
|
10
|
-
import
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
import {
|
|
12
|
+
DEPRECATE_A_USAGE,
|
|
13
|
+
DEPRECATE_COMPUTED_CHAINS,
|
|
14
|
+
DEPRECATE_PROMISE_MANY_ARRAY_BEHAVIORS,
|
|
15
|
+
} from '@ember-data/private-build-infra/deprecations';
|
|
13
16
|
import { StableRecordIdentifier } from '@ember-data/types/q/identifier';
|
|
14
17
|
import type { RecordInstance } from '@ember-data/types/q/record-instance';
|
|
15
18
|
import { FindOptions } from '@ember-data/types/q/store';
|
|
16
19
|
|
|
20
|
+
import type ManyArray from './many-array';
|
|
21
|
+
|
|
17
22
|
export interface HasManyProxyCreateArgs {
|
|
18
23
|
promise: Promise<ManyArray>;
|
|
19
24
|
content?: ManyArray;
|
|
@@ -53,13 +58,27 @@ export default class PromiseManyArray {
|
|
|
53
58
|
this.isDestroyed = false;
|
|
54
59
|
this.isDestroying = false;
|
|
55
60
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
61
|
+
if (DEPRECATE_A_USAGE) {
|
|
62
|
+
const meta = Ember.meta(this);
|
|
63
|
+
meta.hasMixin = (mixin: Object) => {
|
|
64
|
+
deprecate(`Do not use A() on an EmberData PromiseManyArray`, false, {
|
|
65
|
+
id: 'ember-data:no-a-with-array-like',
|
|
66
|
+
until: '5.0',
|
|
67
|
+
since: { enabled: '4.8', available: '4.8' },
|
|
68
|
+
for: 'ember-data',
|
|
69
|
+
});
|
|
70
|
+
// @ts-expect-error ArrayMixin is more than a type
|
|
71
|
+
if (mixin === NativeArray || mixin === ArrayMixin) {
|
|
72
|
+
return true;
|
|
73
|
+
}
|
|
74
|
+
return false;
|
|
75
|
+
};
|
|
76
|
+
} else if (DEBUG) {
|
|
77
|
+
const meta = Ember.meta(this);
|
|
78
|
+
meta.hasMixin = (mixin: Object) => {
|
|
79
|
+
assert(`Do not use A() on an EmberData PromiseManyArray`);
|
|
80
|
+
};
|
|
81
|
+
}
|
|
63
82
|
}
|
|
64
83
|
|
|
65
84
|
//---- Methods/Properties on ArrayProxy that we will keep as our API
|
|
@@ -75,7 +94,9 @@ export default class PromiseManyArray {
|
|
|
75
94
|
get length(): number {
|
|
76
95
|
// shouldn't be needed, but ends up being needed
|
|
77
96
|
// for computed chains even in 4.x
|
|
78
|
-
|
|
97
|
+
if (DEPRECATE_COMPUTED_CHAINS) {
|
|
98
|
+
this['[]'];
|
|
99
|
+
}
|
|
79
100
|
return this.content ? this.content.length : 0;
|
|
80
101
|
}
|
|
81
102
|
|
|
@@ -85,7 +106,9 @@ export default class PromiseManyArray {
|
|
|
85
106
|
// to recompute. We entangle the '[]' tag from
|
|
86
107
|
@dependentKeyCompat
|
|
87
108
|
get '[]'() {
|
|
88
|
-
|
|
109
|
+
if (DEPRECATE_COMPUTED_CHAINS) {
|
|
110
|
+
return this.content?.length && this.content;
|
|
111
|
+
}
|
|
89
112
|
}
|
|
90
113
|
|
|
91
114
|
/**
|
|
@@ -99,7 +122,6 @@ export default class PromiseManyArray {
|
|
|
99
122
|
* @private
|
|
100
123
|
*/
|
|
101
124
|
forEach(cb) {
|
|
102
|
-
this['[]']; // needed for < 3.23 support e.g. 3.20 lts
|
|
103
125
|
if (this.content && this.length) {
|
|
104
126
|
this.content.forEach(cb);
|
|
105
127
|
}
|
|
@@ -105,7 +105,7 @@ export function tagged(_target, key, desc) {
|
|
|
105
105
|
|
|
106
106
|
/**
|
|
107
107
|
Historically EmberData managed a state machine
|
|
108
|
-
for each record, the
|
|
108
|
+
for each record, the localState for which
|
|
109
109
|
was reflected onto Model.
|
|
110
110
|
|
|
111
111
|
This implements the flags and stateName for backwards compat
|
|
@@ -247,10 +247,6 @@ export default class RecordState {
|
|
|
247
247
|
this.notify('isEmpty');
|
|
248
248
|
this.notify('isDirty');
|
|
249
249
|
break;
|
|
250
|
-
case 'unload':
|
|
251
|
-
this.notify('isNew');
|
|
252
|
-
this.notify('isDeleted');
|
|
253
|
-
break;
|
|
254
250
|
case 'errors':
|
|
255
251
|
this.updateInvalidErrors(this.record.errors);
|
|
256
252
|
this.notify('isValid');
|
|
@@ -326,7 +322,7 @@ export default class RecordState {
|
|
|
326
322
|
let rd = this.recordData;
|
|
327
323
|
if (this.isDeleted) {
|
|
328
324
|
assert(`Expected RecordData to implement isDeletionCommitted()`, rd.isDeletionCommitted);
|
|
329
|
-
return rd.isDeletionCommitted();
|
|
325
|
+
return rd.isDeletionCommitted(this.identifier);
|
|
330
326
|
}
|
|
331
327
|
if (this.isNew || this.isEmpty || !this.isValid || this.isDirty || this.isLoading) {
|
|
332
328
|
return false;
|
|
@@ -340,21 +336,21 @@ export default class RecordState {
|
|
|
340
336
|
// TODO this is not actually an RFC'd concept. Determine the
|
|
341
337
|
// correct heuristic to replace this with.
|
|
342
338
|
assert(`Expected RecordData to implement isEmpty()`, rd.isEmpty);
|
|
343
|
-
return !this.isNew && rd.isEmpty();
|
|
339
|
+
return !this.isNew && rd.isEmpty(this.identifier);
|
|
344
340
|
}
|
|
345
341
|
|
|
346
342
|
@tagged
|
|
347
343
|
get isNew() {
|
|
348
344
|
let rd = this.recordData;
|
|
349
345
|
assert(`Expected RecordData to implement isNew()`, rd.isNew);
|
|
350
|
-
return rd.isNew();
|
|
346
|
+
return rd.isNew(this.identifier);
|
|
351
347
|
}
|
|
352
348
|
|
|
353
349
|
@tagged
|
|
354
350
|
get isDeleted() {
|
|
355
351
|
let rd = this.recordData;
|
|
356
352
|
assert(`Expected RecordData to implement isDeleted()`, rd.isDeleted);
|
|
357
|
-
return rd.isDeleted();
|
|
353
|
+
return rd.isDeleted(this.identifier);
|
|
358
354
|
}
|
|
359
355
|
|
|
360
356
|
@tagged
|
|
@@ -365,12 +361,10 @@ export default class RecordState {
|
|
|
365
361
|
@tagged
|
|
366
362
|
get isDirty() {
|
|
367
363
|
let rd = this.recordData;
|
|
368
|
-
|
|
369
|
-
assert(`Expected RecordData to implement isDeletionCommitted()`, rd.isDeletionCommitted);
|
|
370
|
-
if (rd.isDeletionCommitted() || (this.isDeleted && this.isNew)) {
|
|
364
|
+
if (rd.isDeletionCommitted(this.identifier) || (this.isDeleted && this.isNew)) {
|
|
371
365
|
return false;
|
|
372
366
|
}
|
|
373
|
-
return this.isNew || rd.
|
|
367
|
+
return this.isNew || rd.hasChangedAttrs(this.identifier);
|
|
374
368
|
}
|
|
375
369
|
|
|
376
370
|
@tagged
|
|
@@ -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';
|
|
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
14
|
import type { NotificationType } from '@ember-data/store/-private/managers/record-notification-manager';
|
|
12
|
-
import type { DebugWeakCache } from '@ember-data/store/-private/utils/weak-cache';
|
|
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
|
}
|
|
@@ -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,
|
|
@@ -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
|
}
|