Desafío RxJS: Semana 1

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

. , .





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