You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: docs/docs/extensions.md
-1
Original file line number
Diff line number
Diff line change
@@ -9,7 +9,6 @@ It offers extensions like:
9
9
-[⭐️ Devtools](./with-devtools): Integration into Redux Devtools
10
10
-[Conditional Features](./with-conditional): Allows adding features to the store conditionally
11
11
-[DataService](./with-data-service): Builds on top of `withEntities` and adds the backend synchronization to it
12
-
-[Feature Factory](./with-feature-factory): Allows passing properties, methods, or signals from a SignalStore to a custom feature (`signalStoreFeature`).
13
12
-[Immutable State Protection](./with-immutable-state): Protects the state from being mutated outside or inside the Store.
14
13
-[Redux](./with-redux): Possibility to use the Redux Pattern (Reducer, Actions, Effects)
15
14
-[Reset](./with-reset): Adds a `resetState` method to your store
The `withFeatureFactory()` function allows passing properties, methods, or signals from a SignalStore to a feature. It is an advanced feature, primarily targeted for library authors for SignalStore features.
10
-
11
-
Its usage is very simple. It is a function which gets the current store:
`signalStoreFeature` can define input constraints that must be fulfilled by the SignalStore calling the feature. For example, a method `load` needs to be present to fetch data. The default implementation would be:
33
-
34
-
```typescript
35
-
typeEntity= {
36
-
id:number;
37
-
name:string;
38
-
};
39
-
40
-
function withEntityLoader() {
41
-
returnsignalStoreFeature(
42
-
type<{
43
-
methods: {
44
-
load: (id:number) =>Promise<Entity>;
45
-
};
46
-
}>(),
47
-
withState({
48
-
entity: undefinedasEntity|undefined,
49
-
}),
50
-
withMethods((store) => ({
51
-
async setEntityId(id:number) {
52
-
const entity =awaitstore.load(id);
53
-
patchState(store, { entity });
54
-
},
55
-
}))
56
-
);
57
-
}
58
-
```
59
-
60
-
The usage of `withEntityLoader` would be:
61
-
62
-
```typescript
63
-
signalStore(
64
-
withMethods((store) => ({
65
-
load(id:number):Promise<Entity> {
66
-
// some dummy implementation
67
-
returnPromise.resolve({ id, name: 'John' });
68
-
},
69
-
})),
70
-
withEntityLoader()
71
-
);
72
-
```
73
-
74
-
A common issue with generic features is that the input constraints are not fulfilled exactly. If the existing `load` method would return an `Observable<Entity>`, we would have to rename that one and come up with a `load` returning `Promise<Entitiy>`. Renaming an existing method might not always be an option. Beyond that, what if two different features require a `load` method with different return types?
75
-
76
-
Another aspect is that we probably want to encapsulate the load method since it is an internal one. The current options don't allow that, unless the `withEntityLoader` explicitly defines a `_load` method.
77
-
78
-
For example:
79
-
80
-
```typescript
81
-
signalStore(
82
-
withMethods((store) => ({
83
-
load(id:number):Observable<Entity> {
84
-
returnof({ id, name: 'John' });
85
-
},
86
-
})),
87
-
withEntityLoader()
88
-
);
89
-
```
90
-
91
-
`withFeatureFactory` solves those issues by mapping the existing method to whatever `withEntityLoader` requires. `withEntityLoader` needs to move the `load` method dependency to an argument of the function:
92
-
93
-
```typescript
94
-
function withEntityLoader(load: (id:number) =>Promise<Entity>) {
95
-
returnsignalStoreFeature(
96
-
withState({
97
-
entity: undefinedasEntity|undefined,
98
-
}),
99
-
withMethods((store) => ({
100
-
async setEntityId(id:number) {
101
-
const entity =awaitload(id);
102
-
patchState(store, { entity });
103
-
},
104
-
}))
105
-
);
106
-
}
107
-
```
108
-
109
-
`withFeatureFactory` can now map the existing `load` method to the required one.
## Use Case 2: Generic features with Input Constraints
126
-
127
-
Another potential issue with advanced features in a SignalStore is that multiple
128
-
features with input constraints cannot use generic types.
129
-
130
-
For example, `withEntityLoader` is a generic feature that allows the caller to
131
-
define the entity type. Alongside `withEntityLoader`, there's another feature,
132
-
`withOptionalState`, which has input constraints as well.
133
-
134
-
Due to [certain TypeScript limitations](https://ngrx.io/guide/signals/signal-store/custom-store-features#known-typescript-issues),
135
-
the following code will not compile:
136
-
137
-
```typescript
138
-
function withEntityLoader<T>() {
139
-
returnsignalStoreFeature(
140
-
type<{
141
-
methods: {
142
-
load: (id:number) =>Promise<T>;
143
-
};
144
-
}>(),
145
-
withState({
146
-
entity: undefinedasT|undefined,
147
-
}),
148
-
withMethods((store) => ({
149
-
async setEntityId(id:number) {
150
-
const entity =awaitstore.load(id);
151
-
patchState(store, { entity });
152
-
},
153
-
}))
154
-
);
155
-
}
156
-
157
-
function withOptionalState<T>() {
158
-
returnsignalStoreFeature(
159
-
type<{ methods: { foo: () =>string } }>(),
160
-
withState({
161
-
state: undefinedasT|undefined,
162
-
})
163
-
);
164
-
}
13
+
The `withFeatureFactory()` function allows passing properties, methods, or signals from a SignalStore to a feature. It is an advanced feature, primarily targeted for library authors for SignalStore features.
165
14
166
-
signalStore(
167
-
withMethods((store) => ({
168
-
foo: () =>'bar',
169
-
load(id:number):Promise<Entity> {
170
-
// some dummy implementation
171
-
returnPromise.resolve({ id, name: 'John' });
172
-
},
173
-
})),
174
-
withOptionalState<Entity>(),
175
-
withEntityLoader<Entity>()
176
-
);
177
-
```
15
+
:::warning
16
+
## Deprecation
178
17
179
-
Again, `withFeatureFactory` can solve this issue by replacing the input constraint with a function parameter:
18
+
Use `import { withFeature } from '@ngrx/signals'` instead.
[`withFeature`](https://ngrx.io/guide/signals/signal-store/custom-store-features#connecting-a-custom-feature-with-the-store) is the successor of the toolkit's `withFeatureFactory`.
- NgRx [documentation section](https://ngrx.io/guide/signals/signal-store/custom-store-features#connecting-a-custom-feature-with-the-store) on `withFeature`
183
24
184
-
function withEntityLoader<T>(loader: (id:number) =>Promise<T>) {
185
-
returnsignalStoreFeature(
186
-
withState({
187
-
entity: undefinedasT|undefined,
188
-
}),
189
-
withMethods((store) => ({
190
-
async setEntityId(id:number) {
191
-
const entity =awaitloader(id);
192
-
patchState(store, { entity });
193
-
},
194
-
}))
195
-
);
196
-
}
25
+
In the future, `withFeatureFactory` will likely be removed, provided a right migration path is prepared. Watch out for PRs, and see [the PR that deprecates `withFeatureFactory` for the initial plan for handling the removal](https://github.com/angular-architects/ngrx-toolkit/pull/167#pullrequestreview-2735443379).
Copy file name to clipboardExpand all lines: libs/ngrx-toolkit/src/lib/with-feature-factory.ts
+2
Original file line number
Diff line number
Diff line change
@@ -11,6 +11,8 @@ type StoreForFactory<Input extends SignalStoreFeatureResult> = StateSignals<
11
11
Input['methods'];
12
12
13
13
/**
14
+
* @deprecated Use `import { withFeature } from '@ngrx/signals'` instead, starting with `ngrx/signals` 19.1: https://ngrx.io/guide/signals/signal-store/custom-store-features#connecting-a-custom-feature-with-the-store
15
+
*
14
16
* Allows to pass properties, methods, or signals from a SignalStore
0 commit comments