RxJS Challenge: Week 1

Working with Angular willy-nilly, you will use RxJS, because it is at the heart of the framework. It is a very powerful tool for handling events and more. However, not every project makes full use of it. Often these are just requests for backing, simple data transformations and subscriptions. We Roma are very fond RxJS and decided to gather some interesting case studies from our practice. We made something like a challenge for 20 problems out of this, which we propose to solve with RxJS and practice your skills.





Each puzzle will have a boilerplate to make it easy for you to get started. Under the spoiler, I will put a link to my solution and a small explanation to it. In general, tasks will go from simple to complex, and a complete collection with answers and explanations in English is available on GitHub.





​ #1. Observable

. , .





StackBlitz





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));
  }
}
      
      



StackBlitz





​ #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- . .





StackBlitz





​ #3. 5

, . , . , 5 . :





StackBlitz





, , . 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: , , , . , .





StackBlitz





​ #4.

, . , RxJS fetch



:





.





StackBlitz





, , Subject



. — . :





readonly progress$ = this.response$.pipe(filter(Number.isFinite));
      
      



— , :





readonly result$ = this.response$.pipe(
  map(response => typeof response === "string" ? response : null),
  distinctUntilChanged()
);
      
      



StackBlitz





​ #5.

, , . RxJS. CSS, SMS JavaScript ​





StackBlitz





, 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- :





StackBlitz





. 15 RxJS. .








All Articles