Trabajando con Angular, quiera o no, usará RxJS, porque está en el corazón del marco. Es una herramienta muy poderosa para manejar eventos y más. Sin embargo, no todos los proyectos la aprovechan al máximo. A menudo, estas son solo solicitudes de respaldo, transformaciones de datos simples y suscripciones. Los romaníes apreciamos mucho a RxJS y decidimos recopilar algunos estudios de casos interesantes de nuestra práctica. Hicimos algo así como un desafío para 20 problemas con esto, que proponemos resolver con RxJS y practicar sus habilidades.
Cada rompecabezas tendrá un texto estándar para que le resulte más fácil comenzar. Debajo del spoiler, pondré un enlace a mi solución y una pequeña explicación. En general, las tareas irán de simples a complejas, y una colección completa con respuestas y explicaciones en inglés está disponible en 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. .