@ember-data/store 4.12.0-beta.2 → 4.12.0-beta.4
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/README.md +165 -16
- package/addon/-private.js +1 -1
- package/addon/{index-6511184b.js → index-5cfdce8a.js} +1251 -2031
- package/addon/index-5cfdce8a.js.map +1 -0
- package/addon/index.js +1 -1
- package/addon-main.js +18 -15
- package/package.json +14 -13
- package/addon/index-6511184b.js.map +0 -1
package/README.md
CHANGED
|
@@ -1,31 +1,180 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
1
|
+
<p align="center">
|
|
2
|
+
<img
|
|
3
|
+
class="project-logo"
|
|
4
|
+
src="./ember-data-logo-dark.svg#gh-dark-mode-only"
|
|
5
|
+
alt="EmberData Store"
|
|
6
|
+
width="240px"
|
|
7
|
+
title="EmberData Store"
|
|
8
|
+
/>
|
|
9
|
+
<img
|
|
10
|
+
class="project-logo"
|
|
11
|
+
src="./ember-data-logo-light.svg#gh-light-mode-only"
|
|
12
|
+
alt="EmberData Store"
|
|
13
|
+
width="240px"
|
|
14
|
+
title="EmberData Store"
|
|
15
|
+
/>
|
|
16
|
+
</p>
|
|
3
17
|
|
|
4
|
-
|
|
18
|
+
<p align="center">⚡️ The lightweight reactive data library for JavaScript applications</p>
|
|
5
19
|
|
|
20
|
+
This package provides [*Ember***Data**](https://github.com/emberjs/data/)'s `Store` class.
|
|
6
21
|
|
|
7
|
-
|
|
8
|
-
------------------------------------------------------------------------------
|
|
22
|
+
The `Store` coordinates interaction between your application, the `Cache`, and sources of data (such as your `API` or a local persistence layer).
|
|
9
23
|
|
|
10
|
-
|
|
11
|
-
|
|
24
|
+
```mermaid
|
|
25
|
+
flowchart LR
|
|
26
|
+
A[fa:fa-terminal App] ===> D{fa:fa-code-fork Store}
|
|
27
|
+
B{{fa:fa-sitemap RequestManager}} <--> C[(fa:fa-database Source)]
|
|
28
|
+
D <--> E[(fa:fa-archive Cache)]
|
|
29
|
+
D <--> B
|
|
30
|
+
click B href "https://github.com/emberjs/data/tree/main/packages/request" "Go to @ember-data/request" _blank
|
|
31
|
+
click E href "https://github.com/emberjs/data/tree/main/packages/json-api" "Go to @ember-data/json-api" _blank
|
|
32
|
+
style B color:#58a6ff;
|
|
33
|
+
style E color:#58a6ff;
|
|
34
|
+
```
|
|
35
|
+
|
|
36
|
+
Optionally, the Store can be configured to hydrate the response data into rich presentation classes.
|
|
37
|
+
|
|
38
|
+
```mermaid
|
|
39
|
+
flowchart LR
|
|
40
|
+
A[fa:fa-terminal App] --- B(Model)
|
|
41
|
+
A === C{fa:fa-code-fork Store}
|
|
42
|
+
B --- C
|
|
43
|
+
click B href "https://github.com/emberjs/data/tree/main/packages/model" "Go to @ember-data/model" _blank
|
|
44
|
+
style B color:#58a6ff;
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
Install using your javascript package manager of choice. For instance with [pnpm](https://pnpm.io/)
|
|
50
|
+
|
|
51
|
+
```
|
|
52
|
+
pnpm add @ember-data/store
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
After installing you will want to configure your first `Store`. Read more below for how to create and configure stores for your application.
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
## 🔨 Creating A Store
|
|
59
|
+
|
|
60
|
+
To use a `Store` we will need to do few things: add a `Cache` to store data **in-memory**, add an `Adapter` to fetch data from a source, and implement `instantiateRecord` to tell the store how to display the data for individual resources.
|
|
61
|
+
|
|
62
|
+
> **Note** If you are using the package `ember-data` then a `JSON:API` cache and `instantiateRecord` are configured for you by default.
|
|
63
|
+
|
|
64
|
+
### Configuring A Cache
|
|
65
|
+
|
|
66
|
+
To start, let's install a `JSON:API` cache. If your app uses `GraphQL` or `REST` other caches may better fit your data. You can author your own cache by creating one that conforms to the [spec]().
|
|
67
|
+
|
|
68
|
+
The package `@ember-data/json-api` provides a `JSON:API` cache we can use. After installing it, we can configure the store to use this cache.
|
|
69
|
+
|
|
70
|
+
```js
|
|
71
|
+
import Store from '@ember-data/store';
|
|
72
|
+
import Cache from '@ember-data/json-api';
|
|
73
|
+
|
|
74
|
+
class extends Store {
|
|
75
|
+
createCache(storeWrapper) {
|
|
76
|
+
return new Cache(storeWrapper);
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
Now that we have a `cache` let's setup something to handle fetching and saving data via our API.
|
|
12
82
|
|
|
83
|
+
> Note: [1] the cache from `@ember-data/json-api` is a special cache: if the package is present the `createCache` hook will automatically do the above wiring if the hook is not implemented. We still recommend implementing the hook.
|
|
84
|
+
>
|
|
85
|
+
> Note: [2] The `ember-data` package automatically includes the `@ember-data/json-api` cache for you.
|
|
13
86
|
|
|
14
|
-
|
|
15
|
-
------------------------------------------------------------------------------
|
|
87
|
+
### Adding An Adapter
|
|
16
88
|
|
|
89
|
+
When *Ember***Data** needs to fetch or save data it will pass that request to your application's `Adapter` for fulfillment. How this fulfillment occurs (in-memory, device storage, via single or multiple API requests, etc.) is up to that Adapter.
|
|
90
|
+
|
|
91
|
+
To start, let's install a `JSON:API` adapter. If your app uses `GraphQL` or `REST` other adapters may better fit your data. You can author your own adapter by creating one that conforms to the [spec]().
|
|
92
|
+
|
|
93
|
+
The package `@ember-data/adapter` provides a `JSON:API` adapter we can use. After installing it, we can configure the store to use this adapter.
|
|
94
|
+
|
|
95
|
+
```js
|
|
96
|
+
import Store from '@ember-data/store';
|
|
97
|
+
import Adapter from '@ember-data/adapter/json-api';
|
|
98
|
+
|
|
99
|
+
class extends Store {
|
|
100
|
+
#adapter = new Adapter();
|
|
101
|
+
|
|
102
|
+
adapterFor() {
|
|
103
|
+
return this.#adapter;
|
|
104
|
+
}
|
|
105
|
+
}
|
|
17
106
|
```
|
|
18
|
-
|
|
107
|
+
|
|
108
|
+
If you want to know more about using Adapters with Ember read the next section, else lets skip to [Presenting Data from the Cache](#presenting-data-from-the-cache) to configure how our application will interact with our data.
|
|
109
|
+
|
|
110
|
+
#### Using with Ember
|
|
111
|
+
|
|
112
|
+
Note: If you are using Ember and would like to make use of `service` injections in your adapter, you will want to additionally `setOwner` for the Adapter.
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
import Store from '@ember-data/store';
|
|
116
|
+
import Adapter from '@ember-data/adapter/json-api';
|
|
117
|
+
import { getOwner, setOwner } from '@ember/application';
|
|
118
|
+
|
|
119
|
+
class extends Store {
|
|
120
|
+
#adapter = null;
|
|
121
|
+
|
|
122
|
+
adapterFor() {
|
|
123
|
+
let adapter = this.#adapter;
|
|
124
|
+
if (!adapter) {
|
|
125
|
+
const owner = getOwner(this);
|
|
126
|
+
adapter = new Adapter();
|
|
127
|
+
setOwner(adapter, owner);
|
|
128
|
+
this.#adapter = adapter;
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return adapter;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
19
134
|
```
|
|
20
135
|
|
|
136
|
+
By default when using with Ember you only need to implement this hook if you want your adapter usage to be statically analyzeable. *Ember***Data** will attempt to resolve adapters using Ember's resolver. To provide a single Adapter for your application like the above you would provide it as the default export of the file `app/adapters/application.{js/ts}`
|
|
137
|
+
|
|
138
|
+
### Presenting Data from the Cache
|
|
139
|
+
|
|
140
|
+
Now that we have a source and a cach for our data, we need to configure how the Store delivers that data back to our application. We do this via the hook `instantiateRecord`, which allows us to transform the data for a resource before handing it to the application.
|
|
141
|
+
|
|
142
|
+
A naive way to present the data would be to return it as JSON. Typically instead this hook will be used to add reactivity and make each uniue resource a singleton, ensuring that if the cache updates our presented data will reflect the new state.
|
|
143
|
+
|
|
144
|
+
Below is an example of using the hooks `instantiateRecord` and a `teardownRecord` to provide minimal read-only reactive state for simple resources.
|
|
145
|
+
|
|
146
|
+
```ts
|
|
147
|
+
import Store, { recordIdentifierFor } from '@ember-data/store';
|
|
148
|
+
import { TrackedObject } from 'tracked-built-ins';
|
|
149
|
+
|
|
150
|
+
class extends Store {
|
|
151
|
+
instantiateRecord(identifier) {
|
|
152
|
+
const { cache, notifications } = this;
|
|
153
|
+
|
|
154
|
+
// create a TrackedObject with our attributes, id and type
|
|
155
|
+
const record = new TrackedObject(Object.assign({}, cache.peek(identifier)));
|
|
156
|
+
record.type = identifier.type;
|
|
157
|
+
record.id = identifier.id;
|
|
158
|
+
|
|
159
|
+
notifications.subscribe(identifier, (_, change) => {
|
|
160
|
+
if (change === 'attributes') {
|
|
161
|
+
Object.assign(record, cache.peek(identifier));
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
|
|
165
|
+
return record;
|
|
166
|
+
}
|
|
167
|
+
}
|
|
168
|
+
```
|
|
21
169
|
|
|
22
|
-
|
|
23
|
-
------------------------------------------------------------------------------
|
|
170
|
+
Because `instantiateRecord` is opaque to the nature of the record, an implementation can be anything from a fairly simple object to a robust proxy that intelligently links together associated records through relationships.
|
|
24
171
|
|
|
25
|
-
|
|
172
|
+
This also enables creating a record that separates `edit` flows from `create` flows entirely. A record class might choose to implement a `checkout`method that gives access to an editable instance while the primary record continues to be read-only and reflect only persisted (non-mutated) state.
|
|
26
173
|
|
|
174
|
+
Typically you will choose an existing record implementation such as `@ember-data/model` for your application.
|
|
27
175
|
|
|
28
|
-
|
|
29
|
-
------------------------------------------------------------------------------
|
|
176
|
+
Because of the boundaries around instantiation and the cache, record implementations should be capable of interop both with each other and with any `Cache`. Due to this, if needed an application can utilize multiple record implementations and multiple cache implementations either to support enhanced features for only a subset of records or to be able to incrementally migrate from one record/cache to another record or cache.
|
|
30
177
|
|
|
31
|
-
|
|
178
|
+
> Note: [1] `@ember-data/model` is a special record implementation: if the package is present the `instantiateRecord` hook will automatically do the above wiring if the hook is not implemented. Due to the complexity of this legacy package's use of Ember's resolver, we do not recommend wiring this package manually.
|
|
179
|
+
>
|
|
180
|
+
> Note: [2] The `ember-data` package automatically includes the `@ember-data/model` implementation for you.
|
package/addon/-private.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export { C as AdapterPopulatedRecordArray,
|
|
1
|
+
export { C as AdapterPopulatedRecordArray, h as IDENTIFIER_ARRAY_TAG, I as IdentifierArray, M as MUTATE, I as RecordArray, R as RecordArrayManager, g as SOURCE, S as Store, _ as _clearCaches, e as coerceId, j as fastPush, i as isStableIdentifier, n as normalizeModelName, f as notifyArray, p as peekCache, r as recordIdentifierFor, k as removeRecordDataFor, c as setIdentifierForgetMethod, a as setIdentifierGenerationMethod, d as setIdentifierResetMethod, b as setIdentifierUpdateMethod, s as storeFor } from "./index-5cfdce8a";
|