Trabalhando com o Angular quer queira quer não, você usará o RxJS, porque ele está no coração da estrutura. É uma ferramenta muito poderosa para lidar com eventos e muito mais. No entanto, nem todo projeto faz uso completo dele. Freqüentemente, são apenas solicitações de suporte, transformações de dados simples e assinaturas. Nós, Roma, gostamos muito do RxJS e decidimos reunir alguns estudos de caso interessantes da nossa prática. Fizemos algo como um desafio para 20 problemas a partir disso, que nos propomos resolver com RxJS e praticar suas habilidades.
Cada quebra-cabeça terá um clichĂŞ para tornar mais fácil para vocĂŞ começar. Sob o spoiler, colocarei um link para minha solução e uma pequena explicação sobre ela. Em geral, as tarefas vĂŁo de simples a complexas, e uma coleção completa com respostas e explicações em inglĂŞs está disponĂvel no GitHub.
​ #1. Observable
. , .
focusin
focusout
, focus
/blur
. target
relatedTarget
, document.activeElement
— body
. , null
. — . , , : defer(() => of(documentRef.activeElement))
. merge
:
@Injectable()
export class FocusWithinService extends Observable<Element | null> {
constructor(
@Inject(DOCUMENT) documentRef: Document,
{ nativeElement }: ElementRef<HTMLElement>
) {
const focusedElement$ = merge(
defer(() => of(documentRef.activeElement)),
fromEvent(nativeElement, "focusin").pipe(map(({ target }) => target)),
fromEvent(nativeElement, "focusout").pipe(
map(({ relatedTarget }) => relatedTarget)
)
).pipe(
map(element =>
element && nativeElement.contains(element) ? element : null
),
distinctUntilChanged(),
);
super(subscriber => focusedElement$.subscribe(subscriber));
}
}
​ #2.
RxJS — Page Visibility API. InjectionToken. ​
. — . , , . defer
, , , map :
export const PAGE_VISIBILITY = new InjectionToken<Observable<boolean>>(
"Shared Observable based on `document visibility changed`",
{
factory: () => {
const documentRef = inject(DOCUMENT);
return fromEvent(documentRef, "visibilitychange").pipe(
startWith(0),
map(() => documentRef.visibilityState !== "hidden"),
distinctUntilChanged(),
shareReplay()
);
}
}
);
DI- . .
​ #3. 5
, . , . , 5 . :
, , . Subject
, Observable
, :
readonly submit$ = new Subject<void>();
readonly request$ = this.submit$.pipe(
switchMapTo(this.service.pipe(startWith(""))),
share(),
);
. share , .
. , :
readonly user$ = this.request$.pipe(retry());
, , 5 :
readonly error$ = this.request$.pipe(
ignoreElements(),
catchError(e => of(e)),
repeat(),
switchMap(e => timer(5000).pipe(startWith(e)))
);
. repeat retry: , , , . , .
​ #4.
, . , RxJS fetch
:
.
, , Subject
. — . :
readonly progress$ = this.response$.pipe(filter(Number.isFinite));
— , :
readonly result$ = this.response$.pipe(
map(response => typeof response === "string" ? response : null),
distinctUntilChanged()
);
​ #5.
, , . RxJS. CSS, SMS JavaScript ​
, takeWhile
:
function countdownFrom(start: number): Observable<number> {
return timer(0, 1000).pipe(
map(index => start - index),
takeWhile(Boolean, true)
);
}
, 0
, , . switchMapTo
Subject
, , . :
<ng-container *ngIf="countdown$ | async as value else resend">
Resend code in {{ value }} sec.
</ng-container>
<ng-template #resend>
<button (click)="resend$.next()">Resend code</button>
</ng-template>
0 ngIf .
CSS- :active
. CSS- :
. 15 RxJS. .