Skip to content

Instantly share code, notes, and snippets.

@otodockal
Last active April 12, 2023 13:35
Show Gist options
  • Save otodockal/8eae0b52e320a656947e569e93236184 to your computer and use it in GitHub Desktop.
Save otodockal/8eae0b52e320a656947e569e93236184 to your computer and use it in GitHub Desktop.
import { inject, Injectable } from '@angular/core';
import { NonNullableFormBuilder } from '@angular/forms';
import { ComponentStore, OnStateInit, tapResponse } from '@ngrx/component-store';
import { of } from 'rxjs';
import { switchMap, tap, withLatestFrom } from 'rxjs/operators';
export interface User {
id: number;
name: string;
}
export interface UserComponentState {
status: {
state: 'loading' | 'loaded' | 'loaded_last' | null;
type: 'success' | 'error' | null;
};
criteria: {
page: number;
pageSize: number;
query: string | null;
};
response: User[] | null;
}
@Injectable()
export class UserComponentStore extends ComponentStore<UserComponentState> implements OnStateInit {
// connect to URL, if needed
readonly form = inject(NonNullableFormBuilder).group({
query: '',
});
// init state
constructor() {
super({
status: {
state: null,
type: null,
},
criteria: {
page: 1,
pageSize: 20,
query: null,
},
response: null,
});
}
ngrxOnStateInit() {
this.effect(() =>
this.form.valueChanges.pipe(
tap((criteria) => {
this.fetch(criteria);
})
)
);
}
/** SELECTORS */
private readonly status$ = this.select((state) => state.status);
private readonly criteria$ = this.select((state) => state.criteria);
private readonly response$ = this.select((state) => state.response);
readonly vm$ = this.select(
this.status$,
this.criteria$,
this.response$,
(status, criteria, response) => ({
status,
criteria,
response,
}),
{ debounce: true }
);
/** UPDATERS */
readonly setStatus = this.updater((state, status: UserComponentState['status']) => ({
...state,
status,
}));
readonly setCriteria = this.updater(
(state, criteria: Partial<UserComponentState['criteria']>) => ({
...state,
criteria: {
...state.criteria,
...criteria,
},
})
);
readonly setResponse = this.updater((state, response: UserComponentState['response']) => ({
...state,
response,
}));
/** EFFECTS */
readonly fetch = this.effect<Partial<UserComponentState['criteria']> | void>((value$) => {
return value$.pipe(
tap((criteria) => {
if (criteria) {
this.setCriteria(criteria);
}
this.setStatus({
state: 'loading',
type: null,
});
}),
withLatestFrom(this.criteria$),
switchMap(([, criteria]) =>
of(null).pipe(
tapResponse(
(res) => {
this.setResponse(res);
this.setStatus({
state: 'loaded',
type: 'success',
});
},
() => {
this.setStatus({
state: 'loaded',
type: 'error',
});
}
)
)
)
);
});
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment