ember-data-model-fragments 7.0.2 → 8.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.release-plan.json +16 -4
- package/CHANGELOG.md +40 -0
- package/README.md +95 -31
- package/addon/array/fragment.js +19 -0
- package/addon/array/stateful.js +9 -45
- package/addon/attributes/array.js +55 -43
- package/addon/attributes/fragment-array.js +58 -46
- package/addon/attributes/fragment-owner.js +12 -2
- package/addon/attributes/fragment.js +61 -45
- package/addon/cache/fragment-cache.js +323 -24
- package/addon/cache/fragment-record-data-proxy.js +3 -1
- package/addon/cache/fragment-state-manager.js +283 -70
- package/addon/ext.js +52 -216
- package/addon/fragment.js +129 -48
- package/addon/index.js +15 -4
- package/addon/schema-service.js +190 -0
- package/addon/serializer.js +21 -0
- package/addon/serializers/fragment.js +85 -0
- package/addon/serializers/json-api.js +85 -0
- package/addon/serializers/rest.js +83 -0
- package/addon/serializers/utils.js +253 -0
- package/addon/store.js +301 -0
- package/addon/transforms/fragment.js +3 -7
- package/addon/util/fragment-cache.js +59 -0
- package/package.json +76 -65
- package/addon/record-data.js +0 -1131
- package/app/initializers/model-fragments.js +0 -11
- package/record-data.d.ts +0 -4
package/.release-plan.json
CHANGED
|
@@ -1,18 +1,30 @@
|
|
|
1
1
|
{
|
|
2
2
|
"solution": {
|
|
3
3
|
"ember-data-model-fragments": {
|
|
4
|
-
"impact": "
|
|
5
|
-
"oldVersion": "7.0.
|
|
6
|
-
"newVersion": "
|
|
4
|
+
"impact": "major",
|
|
5
|
+
"oldVersion": "7.0.3",
|
|
6
|
+
"newVersion": "8.0.0",
|
|
7
7
|
"tagName": "latest",
|
|
8
8
|
"constraints": [
|
|
9
|
+
{
|
|
10
|
+
"impact": "major",
|
|
11
|
+
"reason": "Appears in changelog section :boom: Breaking Change"
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
"impact": "minor",
|
|
15
|
+
"reason": "Appears in changelog section :rocket: Enhancement"
|
|
16
|
+
},
|
|
9
17
|
{
|
|
10
18
|
"impact": "patch",
|
|
11
19
|
"reason": "Appears in changelog section :bug: Bug Fix"
|
|
20
|
+
},
|
|
21
|
+
{
|
|
22
|
+
"impact": "patch",
|
|
23
|
+
"reason": "Appears in changelog section :house: Internal"
|
|
12
24
|
}
|
|
13
25
|
],
|
|
14
26
|
"pkgJSONPath": "./package.json"
|
|
15
27
|
}
|
|
16
28
|
},
|
|
17
|
-
"description": "## Release (2026-
|
|
29
|
+
"description": "## Release (2026-04-28)\n\n* ember-data-model-fragments 8.0.0 (major)\n\n#### :boom: Breaking Change\n* `ember-data-model-fragments`\n * [#507](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/507) Support ember-data 4.13.x and require extending FragmentStore/FragmentSerializer ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n\n#### :rocket: Enhancement\n* `ember-data-model-fragments`\n * [#523](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/523) Add ember-data 5.x support ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#521](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/521) Import Comparable directly in fragments ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n\n#### :bug: Bug Fix\n* `ember-data-model-fragments`\n * [#526](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/526) Fix fragmentSerialize writing to wrong location for fragment-only models ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#525](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/525) Fix createRecord crash when initializing fragment attrs with fragment instances ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#524](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/524) Fix fragment serializer resolution falling back to application serializer ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n\n#### :house: Internal\n* `ember-data-model-fragments`\n * [#522](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/522) Update release-plan ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#520](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/520) Remove unused legacy record-data implementation ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#519](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/519) Remove old ember-data version checks ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n * [#517](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/517) Various tech debt cleanup ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n\n#### Committers: 1\n- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))\n"
|
|
18
30
|
}
|
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## Release (2026-04-28)
|
|
4
|
+
|
|
5
|
+
* ember-data-model-fragments 8.0.0 (major)
|
|
6
|
+
|
|
7
|
+
#### :boom: Breaking Change
|
|
8
|
+
* `ember-data-model-fragments`
|
|
9
|
+
* [#507](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/507) Support ember-data 4.13.x and require extending FragmentStore/FragmentSerializer ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
10
|
+
|
|
11
|
+
#### :rocket: Enhancement
|
|
12
|
+
* `ember-data-model-fragments`
|
|
13
|
+
* [#523](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/523) Add ember-data 5.x support ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
14
|
+
* [#521](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/521) Import Comparable directly in fragments ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
15
|
+
|
|
16
|
+
#### :bug: Bug Fix
|
|
17
|
+
* `ember-data-model-fragments`
|
|
18
|
+
* [#526](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/526) Fix fragmentSerialize writing to wrong location for fragment-only models ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
19
|
+
* [#525](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/525) Fix createRecord crash when initializing fragment attrs with fragment instances ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
20
|
+
* [#524](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/524) Fix fragment serializer resolution falling back to application serializer ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
21
|
+
|
|
22
|
+
#### :house: Internal
|
|
23
|
+
* `ember-data-model-fragments`
|
|
24
|
+
* [#522](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/522) Update release-plan ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
25
|
+
* [#520](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/520) Remove unused legacy record-data implementation ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
26
|
+
* [#519](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/519) Remove old ember-data version checks ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
27
|
+
* [#517](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/517) Various tech debt cleanup ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
28
|
+
|
|
29
|
+
#### Committers: 1
|
|
30
|
+
- Robbie Wagner ([@RobbieTheWagner](https://github.com/RobbieTheWagner))
|
|
31
|
+
|
|
32
|
+
## Release (2026-02-27)
|
|
33
|
+
|
|
34
|
+
* ember-data-model-fragments 7.0.3 (patch)
|
|
35
|
+
|
|
36
|
+
#### :bug: Bug Fix
|
|
37
|
+
* `ember-data-model-fragments`
|
|
38
|
+
* [#514](https://github.com/adopted-ember-addons/ember-data-model-fragments/pull/514) Guard fragment access on destroyed/unloaded records ([@deanmarano](https://github.com/deanmarano))
|
|
39
|
+
|
|
40
|
+
#### Committers: 1
|
|
41
|
+
- Dean Marano ([@deanmarano](https://github.com/deanmarano))
|
|
42
|
+
|
|
3
43
|
## Release (2026-02-11)
|
|
4
44
|
|
|
5
45
|
* ember-data-model-fragments 7.0.2 (patch)
|
package/README.md
CHANGED
|
@@ -20,20 +20,7 @@ Use the following table to decide which version of this project to use with your
|
|
|
20
20
|
| >= v3.13.x < v3.27.x | v5.x | 12+ |
|
|
21
21
|
| >= v3.28.x < v4.7.x | v6.0.x | 14+ |
|
|
22
22
|
| v4.12.x | v7.x | 18+ |
|
|
23
|
-
|
|
24
|
-
### Upgrading to ember-data 4.12
|
|
25
|
-
|
|
26
|
-
When using ember-data 4.12, you must add the following entry to your app's deprecation workflow to allow the app to boot. This addon uses `reopenClass` internally, which ember-data 4.12 deprecates:
|
|
27
|
-
|
|
28
|
-
```javascript
|
|
29
|
-
// config/deprecation-workflow.js
|
|
30
|
-
self.deprecationWorkflow = self.deprecationWorkflow || {};
|
|
31
|
-
self.deprecationWorkflow.config = {
|
|
32
|
-
workflow: [
|
|
33
|
-
{ handler: "silence", matchId: "ember-data:deprecate-model-reopenclass" },
|
|
34
|
-
],
|
|
35
|
-
};
|
|
36
|
-
```
|
|
23
|
+
| v4.12.x < v6.x | v8.x | 20.19+ |
|
|
37
24
|
|
|
38
25
|
## Installation
|
|
39
26
|
|
|
@@ -51,6 +38,36 @@ $ ember generate fragment foo someAttr:string anotherAttr:boolean
|
|
|
51
38
|
|
|
52
39
|
Which will create the module `app/models/foo.js` which exports a `Fragment` class with the given attributes.
|
|
53
40
|
|
|
41
|
+
## Setup
|
|
42
|
+
|
|
43
|
+
This addon requires you to extend the provided `FragmentStore` and a fragment-aware serializer in your application.
|
|
44
|
+
|
|
45
|
+
### Store
|
|
46
|
+
|
|
47
|
+
Create or update your application's store service to extend `FragmentStore`:
|
|
48
|
+
|
|
49
|
+
```javascript
|
|
50
|
+
// app/services/store.js
|
|
51
|
+
|
|
52
|
+
import FragmentStore from "ember-data-model-fragments/store";
|
|
53
|
+
|
|
54
|
+
export default class Store extends FragmentStore {}
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
### Serializer
|
|
58
|
+
|
|
59
|
+
Create or update your application serializer to extend one of the fragment-aware serializers:
|
|
60
|
+
|
|
61
|
+
```javascript
|
|
62
|
+
// app/serializers/application.js
|
|
63
|
+
|
|
64
|
+
import FragmentSerializer from "ember-data-model-fragments/serializer";
|
|
65
|
+
|
|
66
|
+
export default class ApplicationSerializer extends FragmentSerializer {}
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
See the [Serialization](#serializing) section for more options if you're using `RESTSerializer` or `JSONAPISerializer`.
|
|
70
|
+
|
|
54
71
|
## Example
|
|
55
72
|
|
|
56
73
|
```javascript
|
|
@@ -297,42 +314,89 @@ export default class NameSerializer extends JSONSerializer {
|
|
|
297
314
|
}
|
|
298
315
|
```
|
|
299
316
|
|
|
300
|
-
Since fragment deserialization uses the value of a single attribute in the parent model, the `normalizeResponse` method of the serializer is never used.
|
|
317
|
+
Since fragment deserialization uses the value of a single attribute in the parent model, the `normalizeResponse` method of the serializer is never used. The fragment value is not a full-fledged [JSON:API](http://jsonapi.org/) resource, so a `JSONAPISerializer` cannot be used to normalize a fragment directly. The addon takes care of this for you (see [Fragment serializer resolution](#fragment-serializer-resolution) below) — your application serializer can still be `FragmentJSONAPISerializer` or `FragmentRESTSerializer`.
|
|
301
318
|
|
|
302
|
-
|
|
319
|
+
Your application serializer should extend one of the fragment-aware serializers provided by this addon:
|
|
303
320
|
|
|
304
321
|
```javascript
|
|
305
|
-
// app/serializers/
|
|
322
|
+
// app/serializers/application.js
|
|
306
323
|
|
|
307
|
-
import
|
|
324
|
+
import FragmentSerializer from "ember-data-model-fragments/serializer";
|
|
308
325
|
|
|
309
|
-
export default class
|
|
326
|
+
export default class ApplicationSerializer extends FragmentSerializer {}
|
|
310
327
|
```
|
|
311
328
|
|
|
329
|
+
If you're using `RESTSerializer`, use `FragmentRESTSerializer` instead:
|
|
330
|
+
|
|
312
331
|
```javascript
|
|
313
|
-
// app/
|
|
332
|
+
// app/serializers/application.js
|
|
314
333
|
|
|
315
|
-
import
|
|
334
|
+
import { FragmentRESTSerializer } from "ember-data-model-fragments/serializer";
|
|
335
|
+
|
|
336
|
+
export default class ApplicationSerializer extends FragmentRESTSerializer {}
|
|
337
|
+
```
|
|
338
|
+
|
|
339
|
+
If you're using `JSONAPISerializer`, use `FragmentJSONAPISerializer`:
|
|
340
|
+
|
|
341
|
+
```javascript
|
|
342
|
+
// app/serializers/application.js
|
|
316
343
|
|
|
317
|
-
|
|
318
|
-
|
|
344
|
+
import { FragmentJSONAPISerializer } from "ember-data-model-fragments/serializer";
|
|
345
|
+
|
|
346
|
+
export default class ApplicationSerializer extends FragmentJSONAPISerializer {}
|
|
347
|
+
```
|
|
348
|
+
|
|
349
|
+
### Fragment serializer resolution
|
|
350
|
+
|
|
351
|
+
When this addon needs to (de)serialize a fragment, it looks up a serializer for that fragment's model name in this order:
|
|
352
|
+
|
|
353
|
+
1. **A serializer registered for the specific fragment type** — e.g. `app/serializers/name.js` for an `MF.fragment("name")` attribute. Use this to customize how a single fragment type is (de)serialized (see the `NameSerializer` example above).
|
|
354
|
+
2. **An app-wide fragment serializer registered as `serializer:-fragment`** — use this if you want every fragment in your app to share custom behavior without registering a separate serializer for each fragment type.
|
|
355
|
+
3. **The bundled `FragmentSerializer`** (a `JSONSerializer`-based serializer) — registered automatically the first time it is needed. This is what powers the "just works" behavior for apps that don't customize fragment serialization.
|
|
356
|
+
|
|
357
|
+
Fragment lookups never fall back to `serializer:application`. This is intentional: a typical application serializer is a `RESTSerializer` or `JSONAPISerializer`, neither of which can normalize a raw fragment hash. Your application serializer therefore remains free to be `FragmentRESTSerializer` or `FragmentJSONAPISerializer`, while fragments themselves are always handled by a JSON serializer.
|
|
358
|
+
|
|
359
|
+
#### Customizing serialization for one fragment type
|
|
360
|
+
|
|
361
|
+
```javascript
|
|
362
|
+
// app/serializers/name.js
|
|
363
|
+
|
|
364
|
+
import FragmentSerializer from "ember-data-model-fragments/serializer";
|
|
365
|
+
|
|
366
|
+
export default class NameSerializer extends FragmentSerializer {
|
|
367
|
+
attrs = {
|
|
368
|
+
given: "first",
|
|
369
|
+
family: "last",
|
|
370
|
+
};
|
|
319
371
|
}
|
|
372
|
+
```
|
|
320
373
|
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
374
|
+
#### Customizing serialization for every fragment
|
|
375
|
+
|
|
376
|
+
```javascript
|
|
377
|
+
// app/serializers/-fragment.js
|
|
378
|
+
|
|
379
|
+
import FragmentSerializer from "ember-data-model-fragments/serializer";
|
|
380
|
+
|
|
381
|
+
export default class AppFragmentSerializer extends FragmentSerializer {
|
|
382
|
+
keyForAttribute(key) {
|
|
383
|
+
// e.g. snake_case fragment attribute keys app-wide
|
|
384
|
+
return key.replace(/([A-Z])/g, "_$1").toLowerCase();
|
|
385
|
+
}
|
|
386
|
+
}
|
|
325
387
|
```
|
|
326
388
|
|
|
389
|
+
Per-fragment-type serializers (`app/serializers/<fragment-name>.js`) take precedence over `serializer:-fragment`.
|
|
390
|
+
|
|
327
391
|
If custom serialization of the owner record is needed, fragment [snapshots](http://emberjs.com/api/data/classes/DS.Snapshot.html) can be accessed using the [`Snapshot#attr`](http://emberjs.com/api/data/classes/DS.Snapshot.html#method_attr) method. Note that this differs from how relationships are accessed on snapshots (using `belongsTo`/`hasMany` methods):
|
|
328
392
|
|
|
329
393
|
```javascript
|
|
330
394
|
// apps/serializers/person.js
|
|
331
395
|
// Fragment snapshots are accessed using `snapshot.attr()`
|
|
332
396
|
|
|
333
|
-
import
|
|
397
|
+
import FragmentSerializer from "ember-data-model-fragments/serializer";
|
|
334
398
|
|
|
335
|
-
export default
|
|
399
|
+
export default class PersonSerializer extends FragmentSerializer {
|
|
336
400
|
serialize(snapshot, options) {
|
|
337
401
|
const json = super.serialize(...arguments);
|
|
338
402
|
|
|
@@ -355,8 +419,8 @@ export default JSONSerializer.extend({
|
|
|
355
419
|
json.title_count = titlesSnapshot.length;
|
|
356
420
|
|
|
357
421
|
return json;
|
|
358
|
-
}
|
|
359
|
-
}
|
|
422
|
+
}
|
|
423
|
+
}
|
|
360
424
|
```
|
|
361
425
|
|
|
362
426
|
## Nesting
|
package/addon/array/fragment.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { assert } from '@ember/debug';
|
|
2
2
|
import { typeOf } from '@ember/utils';
|
|
3
|
+
import { dependencySatisfies, macroCondition } from '@embroider/macros';
|
|
3
4
|
import StatefulArray from './stateful';
|
|
4
5
|
import { isFragment, setFragmentOwner } from '../fragment';
|
|
5
6
|
import isInstanceOfType from '../util/instance-of-type';
|
|
@@ -166,10 +167,28 @@ const FragmentArray = StatefulArray.extend({
|
|
|
166
167
|
this.key,
|
|
167
168
|
props,
|
|
168
169
|
);
|
|
170
|
+
if (macroCondition(dependencySatisfies('ember-data', '>=5.8.0'))) {
|
|
171
|
+
const fragment = this.store._instanceCache.getRecord(fragmentIdentifier);
|
|
172
|
+
const definitions = this.store
|
|
173
|
+
.getSchemaDefinitionService()
|
|
174
|
+
.fields(fragmentIdentifier);
|
|
175
|
+
|
|
176
|
+
if (props) {
|
|
177
|
+
for (const [key, value] of Object.entries(props)) {
|
|
178
|
+
if (!definitions.has(key)) {
|
|
179
|
+
fragment.set(key, value);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
}
|
|
183
|
+
|
|
184
|
+
return this.pushObject(fragment);
|
|
185
|
+
}
|
|
186
|
+
|
|
169
187
|
const fragment = this.store._instanceCache.getRecord(
|
|
170
188
|
fragmentIdentifier,
|
|
171
189
|
props,
|
|
172
190
|
);
|
|
191
|
+
|
|
173
192
|
return this.pushObject(fragment);
|
|
174
193
|
},
|
|
175
194
|
});
|
package/addon/array/stateful.js
CHANGED
|
@@ -2,24 +2,13 @@ import EmberObject, { get } from '@ember/object';
|
|
|
2
2
|
import { isArray } from '@ember/array';
|
|
3
3
|
import MutableArray from '@ember/array/mutable';
|
|
4
4
|
import { assert } from '@ember/debug';
|
|
5
|
-
import { diffArray } from '@ember-data/model/-private';
|
|
6
5
|
import { copy } from '../util/copy';
|
|
7
|
-
import
|
|
6
|
+
import fragmentCacheFor from '../util/fragment-cache';
|
|
8
7
|
|
|
9
8
|
/**
|
|
10
9
|
@module ember-data-model-fragments
|
|
11
10
|
*/
|
|
12
11
|
|
|
13
|
-
/**
|
|
14
|
-
* Whether the current version of ember supports array observers.
|
|
15
|
-
* Array observers were deprecated in ember 3.26 and removed in 4.0.
|
|
16
|
-
* @see https://deprecations.emberjs.com/v3.x#toc_array-observers
|
|
17
|
-
* @see https://github.com/emberjs/ember.js/pull/19833
|
|
18
|
-
* @type {boolean}
|
|
19
|
-
* @private
|
|
20
|
-
*/
|
|
21
|
-
export const HAS_ARRAY_OBSERVERS = !gte('4.0.0');
|
|
22
|
-
|
|
23
12
|
/**
|
|
24
13
|
A state-aware array that is tied to an attribute of a `DS.Model` instance.
|
|
25
14
|
|
|
@@ -73,7 +62,7 @@ const StatefulArray = EmberObject.extend(MutableArray, {
|
|
|
73
62
|
@private
|
|
74
63
|
*/
|
|
75
64
|
get cache() {
|
|
76
|
-
return this.store
|
|
65
|
+
return fragmentCacheFor(this.store);
|
|
77
66
|
},
|
|
78
67
|
|
|
79
68
|
init() {
|
|
@@ -88,14 +77,10 @@ const StatefulArray = EmberObject.extend(MutableArray, {
|
|
|
88
77
|
|
|
89
78
|
notify() {
|
|
90
79
|
this._isDirty = true;
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
this.notifyPropertyChange('[]');
|
|
96
|
-
this.notifyPropertyChange('firstObject');
|
|
97
|
-
this.notifyPropertyChange('lastObject');
|
|
98
|
-
}
|
|
80
|
+
this._hasNotified = true;
|
|
81
|
+
this.notifyPropertyChange('[]');
|
|
82
|
+
this.notifyPropertyChange('firstObject');
|
|
83
|
+
this.notifyPropertyChange('lastObject');
|
|
99
84
|
},
|
|
100
85
|
|
|
101
86
|
get length() {
|
|
@@ -181,30 +166,9 @@ const StatefulArray = EmberObject.extend(MutableArray, {
|
|
|
181
166
|
|
|
182
167
|
this._isDirty = false;
|
|
183
168
|
this._isUpdating = true;
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
// it's null if no change found
|
|
188
|
-
if (diff.firstChangeIndex !== null) {
|
|
189
|
-
// we found a change
|
|
190
|
-
this.arrayContentWillChange(
|
|
191
|
-
diff.firstChangeIndex,
|
|
192
|
-
diff.removedCount,
|
|
193
|
-
diff.addedCount,
|
|
194
|
-
);
|
|
195
|
-
this._length = currentState.length;
|
|
196
|
-
this.currentState = currentState;
|
|
197
|
-
this.arrayContentDidChange(
|
|
198
|
-
diff.firstChangeIndex,
|
|
199
|
-
diff.removedCount,
|
|
200
|
-
diff.addedCount,
|
|
201
|
-
);
|
|
202
|
-
}
|
|
203
|
-
} else {
|
|
204
|
-
this._hasNotified = false;
|
|
205
|
-
this._length = currentState.length;
|
|
206
|
-
this.currentState = currentState;
|
|
207
|
-
}
|
|
169
|
+
this._hasNotified = false;
|
|
170
|
+
this._length = currentState.length;
|
|
171
|
+
this.currentState = currentState;
|
|
208
172
|
this._isUpdating = false;
|
|
209
173
|
},
|
|
210
174
|
|
|
@@ -3,6 +3,7 @@ import { isArray } from '@ember/array';
|
|
|
3
3
|
import { assert } from '@ember/debug';
|
|
4
4
|
import { recordIdentifierFor } from '@ember-data/store';
|
|
5
5
|
import metaTypeFor from '../util/meta-type-for';
|
|
6
|
+
import fragmentCacheFor from '../util/fragment-cache';
|
|
6
7
|
import StatefulArray from '../array/stateful';
|
|
7
8
|
|
|
8
9
|
/**
|
|
@@ -52,48 +53,59 @@ export default function array(type, options) {
|
|
|
52
53
|
options,
|
|
53
54
|
};
|
|
54
55
|
|
|
55
|
-
//
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
cache.
|
|
71
|
-
|
|
72
|
-
|
|
56
|
+
// Use computed with a dependency on hasDirtyAttributes which changes on rollback
|
|
57
|
+
// This ensures the computed property is re-evaluated when dirty state changes
|
|
58
|
+
return computed(
|
|
59
|
+
'currentState',
|
|
60
|
+
'hasDirtyAttributes',
|
|
61
|
+
'isDestroyed',
|
|
62
|
+
'isDestroying',
|
|
63
|
+
'store.cache',
|
|
64
|
+
{
|
|
65
|
+
get(key) {
|
|
66
|
+
if (this.isDestroying || this.isDestroyed) {
|
|
67
|
+
return null;
|
|
68
|
+
}
|
|
69
|
+
const identifier = recordIdentifierFor(this);
|
|
70
|
+
const cache = fragmentCacheFor(this.store);
|
|
71
|
+
if (cache.getFragment(identifier, key) === null) {
|
|
72
|
+
return null;
|
|
73
|
+
}
|
|
74
|
+
let array = cache.getFragmentArrayCache(identifier, key);
|
|
75
|
+
if (!array) {
|
|
76
|
+
array = StatefulArray.create({
|
|
77
|
+
store: this.store,
|
|
78
|
+
identifier,
|
|
79
|
+
key,
|
|
80
|
+
});
|
|
81
|
+
cache.setFragmentArrayCache(identifier, key, array);
|
|
82
|
+
}
|
|
83
|
+
return array;
|
|
84
|
+
},
|
|
85
|
+
set(key, value) {
|
|
86
|
+
assert(
|
|
87
|
+
'You must pass an array or null to set an array',
|
|
88
|
+
value === null || isArray(value),
|
|
89
|
+
);
|
|
90
|
+
const identifier = recordIdentifierFor(this);
|
|
91
|
+
const cache = fragmentCacheFor(this.store);
|
|
92
|
+
if (value === null) {
|
|
93
|
+
cache.setDirtyFragment(identifier, key, null);
|
|
94
|
+
return null;
|
|
95
|
+
}
|
|
96
|
+
cache.setDirtyFragment(identifier, key, value.slice());
|
|
97
|
+
let array = cache.getFragmentArrayCache(identifier, key);
|
|
98
|
+
if (!array) {
|
|
99
|
+
array = StatefulArray.create({
|
|
100
|
+
store: this.store,
|
|
101
|
+
identifier,
|
|
102
|
+
key,
|
|
103
|
+
});
|
|
104
|
+
cache.setFragmentArrayCache(identifier, key, array);
|
|
105
|
+
}
|
|
106
|
+
array._setFragments(value);
|
|
107
|
+
return array;
|
|
108
|
+
},
|
|
73
109
|
},
|
|
74
|
-
|
|
75
|
-
assert(
|
|
76
|
-
'You must pass an array or null to set an array',
|
|
77
|
-
value === null || isArray(value),
|
|
78
|
-
);
|
|
79
|
-
const identifier = recordIdentifierFor(this);
|
|
80
|
-
const cache = this.store.cache;
|
|
81
|
-
if (value === null) {
|
|
82
|
-
cache.setDirtyFragment(identifier, key, null);
|
|
83
|
-
return null;
|
|
84
|
-
}
|
|
85
|
-
cache.setDirtyFragment(identifier, key, value.slice());
|
|
86
|
-
let array = cache.getFragmentArrayCache(identifier, key);
|
|
87
|
-
if (!array) {
|
|
88
|
-
array = StatefulArray.create({
|
|
89
|
-
store: this.store,
|
|
90
|
-
identifier,
|
|
91
|
-
key,
|
|
92
|
-
});
|
|
93
|
-
cache.setFragmentArrayCache(identifier, key, array);
|
|
94
|
-
}
|
|
95
|
-
array._setFragments(value);
|
|
96
|
-
return array;
|
|
97
|
-
},
|
|
98
|
-
}).meta(meta);
|
|
110
|
+
).meta(meta);
|
|
99
111
|
}
|
|
@@ -4,6 +4,7 @@ import { typeOf } from '@ember/utils';
|
|
|
4
4
|
import { isArray } from '@ember/array';
|
|
5
5
|
import { recordIdentifierFor } from '@ember-data/store';
|
|
6
6
|
import { isFragment } from '../fragment';
|
|
7
|
+
import fragmentCacheFor from '../util/fragment-cache';
|
|
7
8
|
import metaTypeFor from '../util/meta-type-for';
|
|
8
9
|
import FragmentArray from '../array/fragment';
|
|
9
10
|
|
|
@@ -61,51 +62,62 @@ export default function fragmentArray(type, options) {
|
|
|
61
62
|
options,
|
|
62
63
|
};
|
|
63
64
|
|
|
64
|
-
//
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
65
|
+
// Use computed with a dependency on hasDirtyAttributes which changes on rollback
|
|
66
|
+
// This ensures the computed property is re-evaluated when dirty state changes
|
|
67
|
+
return computed(
|
|
68
|
+
'currentState',
|
|
69
|
+
'hasDirtyAttributes',
|
|
70
|
+
'isDestroyed',
|
|
71
|
+
'isDestroying',
|
|
72
|
+
'store.cache',
|
|
73
|
+
{
|
|
74
|
+
get(key) {
|
|
75
|
+
if (this.isDestroying || this.isDestroyed) {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
const identifier = recordIdentifierFor(this);
|
|
79
|
+
const cache = fragmentCacheFor(this.store);
|
|
80
|
+
if (cache.getFragment(identifier, key) === null) {
|
|
81
|
+
return null;
|
|
82
|
+
}
|
|
83
|
+
let fragmentArray = cache.getFragmentArrayCache(identifier, key);
|
|
84
|
+
if (!fragmentArray) {
|
|
85
|
+
fragmentArray = FragmentArray.create({
|
|
86
|
+
modelName: type,
|
|
87
|
+
store: this.store,
|
|
88
|
+
identifier,
|
|
89
|
+
key,
|
|
90
|
+
});
|
|
91
|
+
cache.setFragmentArrayCache(identifier, key, fragmentArray);
|
|
92
|
+
}
|
|
93
|
+
return fragmentArray;
|
|
94
|
+
},
|
|
95
|
+
set(key, value) {
|
|
96
|
+
assert(
|
|
97
|
+
'You must pass an array of fragments, or null to set a fragmentArray',
|
|
98
|
+
value === null ||
|
|
99
|
+
(isArray(value) &&
|
|
100
|
+
value.every((v) => isFragment(v) || typeOf(v) === 'object')),
|
|
101
|
+
);
|
|
102
|
+
const identifier = recordIdentifierFor(this);
|
|
103
|
+
const cache = fragmentCacheFor(this.store);
|
|
104
|
+
if (value === null) {
|
|
105
|
+
cache.setDirtyFragment(identifier, key, null);
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
let fragmentArray = cache.getFragmentArrayCache(identifier, key);
|
|
109
|
+
if (!fragmentArray) {
|
|
110
|
+
fragmentArray = FragmentArray.create({
|
|
111
|
+
modelName: type,
|
|
112
|
+
store: this.store,
|
|
113
|
+
identifier,
|
|
114
|
+
key,
|
|
115
|
+
});
|
|
116
|
+
cache.setFragmentArrayCache(identifier, key, fragmentArray);
|
|
117
|
+
}
|
|
118
|
+
fragmentArray._setFragments(value);
|
|
119
|
+
return fragmentArray;
|
|
120
|
+
},
|
|
83
121
|
},
|
|
84
|
-
|
|
85
|
-
assert(
|
|
86
|
-
'You must pass an array of fragments, or null to set a fragmentArray',
|
|
87
|
-
value === null ||
|
|
88
|
-
(isArray(value) &&
|
|
89
|
-
value.every((v) => isFragment(v) || typeOf(v) === 'object')),
|
|
90
|
-
);
|
|
91
|
-
const identifier = recordIdentifierFor(this);
|
|
92
|
-
const cache = this.store.cache;
|
|
93
|
-
if (value === null) {
|
|
94
|
-
cache.setDirtyFragment(identifier, key, null);
|
|
95
|
-
return null;
|
|
96
|
-
}
|
|
97
|
-
let fragmentArray = cache.getFragmentArrayCache(identifier, key);
|
|
98
|
-
if (!fragmentArray) {
|
|
99
|
-
fragmentArray = FragmentArray.create({
|
|
100
|
-
modelName: type,
|
|
101
|
-
store: this.store,
|
|
102
|
-
identifier,
|
|
103
|
-
key,
|
|
104
|
-
});
|
|
105
|
-
cache.setFragmentArrayCache(identifier, key, fragmentArray);
|
|
106
|
-
}
|
|
107
|
-
fragmentArray._setFragments(value);
|
|
108
|
-
return fragmentArray;
|
|
109
|
-
},
|
|
110
|
-
}).meta(meta);
|
|
122
|
+
).meta(meta);
|
|
111
123
|
}
|
|
@@ -1,7 +1,9 @@
|
|
|
1
1
|
import { assert } from '@ember/debug';
|
|
2
2
|
import { computed } from '@ember/object';
|
|
3
|
+
import { isDestroying, isDestroyed } from '@ember/destroyable';
|
|
3
4
|
import { isFragment } from '../fragment';
|
|
4
5
|
import { recordIdentifierFor } from '@ember-data/store';
|
|
6
|
+
import fragmentCacheFor from '../util/fragment-cache';
|
|
5
7
|
|
|
6
8
|
/**
|
|
7
9
|
`MF.fragmentOwner` defines a read-only attribute on a `MF.Fragment`
|
|
@@ -27,13 +29,21 @@ import { recordIdentifierFor } from '@ember-data/store';
|
|
|
27
29
|
@return {Attribute}
|
|
28
30
|
*/
|
|
29
31
|
export default function fragmentOwner() {
|
|
30
|
-
|
|
32
|
+
// No dependent keys: the value is invalidated via notifyPropertyChange in
|
|
33
|
+
// setFragmentOwner(). Omitting dependent keys also avoids Ember's
|
|
34
|
+
// "Attempted to access the computed ... on a destroyed object" assertion
|
|
35
|
+
// when a fragment is torn down.
|
|
36
|
+
// eslint-disable-next-line ember/require-computed-property-dependencies
|
|
37
|
+
return computed(function () {
|
|
38
|
+
if (isDestroying(this) || isDestroyed(this)) {
|
|
39
|
+
return null;
|
|
40
|
+
}
|
|
31
41
|
assert(
|
|
32
42
|
'Fragment owner properties can only be used on fragments.',
|
|
33
43
|
isFragment(this),
|
|
34
44
|
);
|
|
35
45
|
const identifier = recordIdentifierFor(this);
|
|
36
|
-
const owner = this.store.
|
|
46
|
+
const owner = fragmentCacheFor(this.store).getFragmentOwner(identifier);
|
|
37
47
|
if (!owner) {
|
|
38
48
|
return null;
|
|
39
49
|
}
|