Wenn Sie wohl oder übel mit Angular arbeiten, verwenden Sie RxJS, da es das Herzstück des Frameworks darstellt. Es ist ein sehr leistungsfähiges Werkzeug für die Behandlung von Ereignissen und mehr. Nicht jedes Projekt nutzt es jedoch in vollem Umfang. Oft sind dies nur Anfragen zur Sicherung, einfache Datentransformationen und Abonnements. Wir Roma lieben RxJS sehr und haben beschlossen, einige interessante Fallstudien aus unserer Praxis zu sammeln. Wir haben daraus eine Herausforderung für 20 Probleme gemacht, die wir mit RxJS lösen und Ihre Fähigkeiten üben möchten.
Jedes Puzzle verfügt über eine Boilerplate, die Ihnen den Einstieg erleichtert. Unter dem Spoiler werde ich einen Link zu meiner Lösung und eine kleine Erklärung dazu setzen. Im Allgemeinen reichen die Aufgaben von einfach bis komplex, und auf GitHub ist eine vollständige Sammlung mit Antworten und Erklärungen auf Englisch verfügbar.
#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. .