Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit eb3bad8

Browse files
docs: update actions doc #1561
1 parent addc3bd commit eb3bad8

File tree

1 file changed

+78
-72
lines changed

1 file changed

+78
-72
lines changed

apps/docs/docs/state/actions/actions.mdx

Lines changed: 78 additions & 72 deletions
Original file line numberDiff line numberDiff line change
@@ -38,51 +38,58 @@ A demo application is available on [GitHub](https://github.com/BioPhoton/rx-angu
3838

3939
## Motivation
4040

41-
Signals, or a more commonly used name actions, are a common part of state management and reactive systems in general.
41+
Operations, or a more commonly used name actions, are a common part of state management and reactive systems in general.
4242
Even if `@rx-angular/state` provides `set` method, sometimes you need to add behavior to your user input or incoming events.
4343

4444
Subjects are normally used to implement this feature. This leads, especially in bigger applications, to a messy code that is bloated with Subjects.
4545

4646
Let's have a look at this piece of code:
4747

4848
```typescript
49+
import { Component } from '@angular/core';
50+
import { Subject } from 'rxjs';
51+
import { withLatestFrom, switchMap } from 'rxjs/operators';
52+
4953
@Component({
54+
selector: 'my-component',
5055
template: `
5156
<input (input)="searchInput($event?.target?.value)" /> Search for:
5257
{{ search$ | async }}<br />
5358
<button (click)="submitBtn()">
54-
Submit<button>
55-
<br />
56-
<ul>
57-
<li *ngFor="let item of list$ | async as list">{{ item }}</li>
58-
</ul>
59-
</button>
59+
Submit
6060
</button>
61+
<br />
62+
<ul>
63+
<li *ngFor="let item of list$ | async">{{ item }}</li>
64+
</ul>
6165
`,
6266
})
63-
class Component {
67+
export class MyComponent {
6468
private _submitBtn = new Subject<void>();
6569
private _searchInput = new Subject<string>();
6670

67-
set submitBtn() {
68-
_submitBtn.next();
71+
submitBtn() {
72+
this._submitBtn.next();
6973
}
74+
7075
get submitBtn$() {
71-
return _searchInput.asObservable();
76+
return this._submitBtn.asObservable();
7277
}
73-
set search(search: string) {
74-
_searchInput.next(search);
78+
79+
searchInput(value: string) {
80+
this._searchInput.next(value);
7581
}
82+
7683
get search$() {
77-
return _searchInput.asObservable();
84+
return this._searchInput.asObservable();
7885
}
7986

8087
list$ = this.submitBtn$.pipe(
8188
withLatestFrom(this.search$),
8289
switchMap(([_, searchString]) => this.api.query(searchString))
8390
);
8491

85-
constructor(api: API) {}
92+
constructor(private api: API) {}
8693
}
8794
```
8895

@@ -110,26 +117,26 @@ interface UiActions {
110117
<input (input)="ui.searchInput($event)" /> Search for:
111118
{{ ui.search$ | async }}<br />
112119
<button (click)="ui.submitBtn()">
113-
Submit<button>
114-
<br />
115-
<ul>
116-
<li *ngFor="let item of list$ | async as list">{{ item }}</li>
117-
</ul>
118-
</button>
120+
Submit
119121
</button>
122+
<br />
123+
<ul>
124+
<li *ngFor="let item of list$ | async as list">{{ item }}</li>
125+
</ul>
120126
`,
121127
providers: [RxActionFactory],
122128
})
123-
class Component {
124-
ui = this.factory.create({ searchInput: (e) => e.target.value });
129+
export class Component {
130+
ui = this.factory.create({ searchInput: (e) => e.target ? e.target.value : '' });
125131

126132
list$ = this.ui.submitBtn$.pipe(
127133
withLatestFrom(this.ui.search$),
128134
switchMap(([_, searchString]) => this.api.query(searchString))
129135
);
130136

131-
constructor(api: API, private factory: RxActionFactory<UiActions>) {}
137+
constructor(private api: API, private factory: RxActionFactory<UiActions>) {}
132138
}
139+
133140
```
134141

135142
## RxAngular Actions
@@ -155,7 +162,7 @@ yarn add @rx-angular/state
155162
By using RxAngular Actions we can reduce the boilerplate significantly, to do so we can start by thinking about the specific section their events and event payload types:
156163

157164
```typescript
158-
interface Commands {
165+
interface Actions {
159166
refreshUser: string | number;
160167
refreshList: string | number;
161168
refreshGenres: string | number;
@@ -165,40 +172,35 @@ interface Commands {
165172
Next we can use the typing to create the action object:
166173

167174
```typescript
168-
commands = getActions<Commands>();
169-
```
170-
171-
The object can now be used to emit signals over setters:
172-
173-
```typescript
174-
commands.refreshUser(value);
175-
commands.refreshList(value);
176-
commands.refreshGenres(value);
175+
const actionsFactory = new RxActionFactory<Actions>();
176+
const actions = actionsFactory.create();
177177
```
178178

179-
The emitted signals can be received over observable properties:
179+
The object can now be used to emit operations over setters:
180180

181181
```typescript
182-
refreshUser$ = commands.refreshUser$;
183-
refreshList$ = commands.refreshList$;
184-
refreshGenres$ = commands.refreshGenres$;
182+
actions.refreshUser(value);
183+
actions.refreshList(value);
184+
actions.refreshGenres(value);
185185
```
186186

187-
You can also emit multiple signals at once:
187+
The emitted operations can be received over observable properties:
188188

189189
```typescript
190-
commands({ refreshUser: true, refreshList: true });
190+
const refreshUser$ = actions.refreshUser$;
191+
const refreshList$ = actions.refreshList$;
192+
const refreshGenres$ = actions.refreshGenres$;
191193
```
192194

193-
If there is the need to make a combined signal you can also select multiple signals and get their emissions in one stream:
195+
If there is the need to make a combined operation you can also select multiple operations and get their emissions in one stream:
194196

195197
```typescript
196-
refreshUserOrList$ = commands.$(['refreshUser', 'refreshList']);
198+
const refreshUserOrList$ = concat(actions.refreshUser$, actions.refreshList$);
197199
```
198200

199-
### Signals in components
201+
### Operations in components
200202

201-
In components/templates we can use signals to map user interaction as well as programmatic to effects or state changes.
203+
In components/templates we can use operations to map user interaction as well as programmatic to effects or state changes.
202204
This reduces the component class code as well as template.
203205

204206
In addition, we can use it as a shorthand in the template and directly connect to action dispatching in the class.
@@ -211,8 +213,8 @@ interface UiActions {
211213

212214
@Component({
213215
template: `
214-
<input (input)="ui.searchInput($event)" /> Search for:
215-
{{ ui.search$ | async }}<br />
216+
<input (input)="ui.searchInput($event.target.value)" /> Search for:
217+
{{ ui.searchInput$ | async }}<br />
216218
<button (click)="ui.submitBtn()">
217219
Submit<button>
218220
<br />
@@ -225,10 +227,10 @@ interface UiActions {
225227
providers: [RxState, RxActionFactory],
226228
})
227229
class Component {
228-
ui = this.factory.create({ searchInput: (e) => e?.target?.value });
230+
ui = this.factory.create<UiActions>({ searchInput: (e) => e });
229231
list$ = this.state.select('list');
230232
submittedSearchQuery$ = this.ui.submitBtn$.pipe(
231-
withLatestFrom(this.ui.search$),
233+
withLatestFrom(this.ui.searchInput$),
232234
map(([_, search]) => search),
233235
debounceTime(1500)
234236
);
@@ -238,14 +240,14 @@ class Component {
238240
private factory: RxActionFactory<UiActions>,
239241
globalState: StateService
240242
) {
241-
super();
242243
this.connect('list', this.globalState.refreshGenres$);
243244

244245
this.state.hold(this.submittedSearchQuery$, this.globalState.refreshGenres);
245246
// Optional reactively:
246247
// this.globalState.connectRefreshGenres(this.submittedSearchQuery$);
247248
}
248249
}
250+
249251
```
250252

251253
#### Using transforms
@@ -265,43 +267,46 @@ You can write you own transforms or use the predefined functions:
265267
- eventValue
266268

267269
```typescript
270+
import { Component } from '@angular/core';
271+
import { RxState, RxActionFactory } from '@rx-angular/state';
272+
import { StateService } from './state.service';
273+
import { withLatestFrom, map, debounceTime } from 'rxjs/operators';
274+
268275
interface UiActions {
269276
submitBtn: void;
270277
searchInput: string;
271278
}
272279

273280
@Component({
281+
selector: 'my-component',
274282
template: `
275283
<input (input)="ui.searchInput($event)" /> Search for:
276-
{{ ui.search$ | async }}<br />
284+
{{ ui.searchInput$ | async }}<br />
277285
<button (click)="ui.submitBtn()">
278-
Submit<button>
279-
<br />
280-
<ul>
281-
<li *ngFor="let item of list$ | async as list">{{ item }}</li>
282-
</ul>
283-
</button>
286+
Submit
284287
</button>
288+
<br />
289+
<ul>
290+
<li *ngFor="let item of list$ | async">{{ item }}</li>
291+
</ul>
285292
`,
286293
providers: [RxState, RxActionFactory],
287294
})
288-
class Component {
289-
// (e) => e.target ? e.target.value : e
290-
ui = this.factory.create({ searchInput: eventValue });
295+
export class MyComponent {
296+
ui = this.factory.create<UiActions>({ searchInput: eventValue });
291297
list$ = this.state.select('list');
292298
submittedSearchQuery$ = this.ui.submitBtn$.pipe(
293-
withLatestFrom(this.ui.search$),
299+
withLatestFrom(this.ui.searchInput$),
294300
map(([_, search]) => search),
295301
debounceTime(1500)
296302
);
297303

298304
constructor(
299305
private state: RxState<State>,
300306
private factory: RxActionFactory<UiActions>,
301-
globalState: StateService
307+
private globalState: StateService
302308
) {
303-
super();
304-
this.connect('list', this.globalState.refreshGenres$);
309+
this.state.connect('list', this.globalState.refreshGenres$);
305310

306311
this.state.hold(this.submittedSearchQuery$, this.globalState.refreshGenres);
307312
// Optional reactively:
@@ -321,15 +326,15 @@ interface State {
321326
genres: MovieGenreModel[];
322327
}
323328

324-
interface Commands {
329+
interface Actions {
325330
refreshGenres: string | number;
326331
}
327332

328333
@Injectable({
329334
providedIn: 'root',
330335
})
331336
export class StateService extends RxState<State> {
332-
private commands = new RxActionFactory<Commands>().create();
337+
private actions = new RxActionFactory<Actions>().create();
333338

334339
genres$ = this.select('genres');
335340

@@ -338,20 +343,21 @@ export class StateService extends RxState<State> {
338343

339344
this.connect(
340345
'genres',
341-
this.commands.fetchGenres$.pipe(exhaustMap(this.tmdb2Service.getGenres))
346+
this.actions.refreshGenres$.pipe(exhaustMap(this.tmdb2Service.getGenres))
342347
);
343348
}
344349

345-
refreshGenres(genre: string): void {
346-
this.commands.fetchGenres(genre);
350+
refreshGenres(genre: string | number): void {
351+
this.actions.refreshGenres(genre);
347352
}
348353

349354
// Optionally the reactive way
350-
connectRefreshGenres(genre$: Observable<string>): void {
351-
this.connect(
352-
'genres',
353-
this.commands.fetchGenres$.pipe(exhaustMap(this.tmdb2Service.getGenres))
355+
connectRefreshGenres(genre$: Observable<string | number>): void {
356+
this.hold(
357+
genre$,
358+
genre => this.actions.refreshGenres(genre)
354359
);
355360
}
356361
}
362+
357363
```

0 commit comments

Comments
 (0)