Desafio RxJS: Semana 1

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

. , .





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