Is the async pipe the best choice for handling Observables in a polling scenario

The situation at hand:

I currently have a service that continuously polls a specific URL every 2 seconds:

export class FooDataService {

...

  public provideFooData() {
    const interval = Observable.interval(2000).startWith(0);
    return interval
      .switchMap(() => this.http.get(this.requestUrl))
      .map(fooData => fooData.json())
  }
}

Now, in one of my components I need to display this data obtained through polling:

export class FooComponent implements OnInit {

  constructor(private fooDataService: FooDataService) {}

  public fooData$: Observable<FooData>;

  ngOnInit() {
    this.fooData$ = this.fooDataService.provideFooData();
  }
}

Within the component template, I utilize an async pipe to retrieve the polled data and pass it to a sub-component:

<foo-data-viewer [data]="fooData$ | async"></foo-data-viewer>

An issue with the current setup:

The way this functionality is implemented makes it challenging to test using protractor (refer to my previous question on the topic or this article). Due to all code being executed within the ngZone, protractor will wait for all queued actions to complete before proceeding. However, Observable.interval() queues an indefinite number of actions, resulting in protractor timing out.

A commonly suggested solution:

The most frequently recommended approach involves utilizing runOutsideAngular like so:

export class FooComponent implements OnInit, OnDestroy {

  constructor(private ngZone: NgZone,
              private fooDataService: FooDataService) {}

  public fooData: FooData;
  public fooDataSubscription: Subscription<FooData>;

  ngOnInit() {

    this.ngZone.runOutsideAngular(() => {
      this.fooDataSubscription =
        this.fooDataService.provideFooData()
            .subscribe(
               fooData => this.ngZone.run(() => this.fooData = fooData)
            );
    });
  }

  ngOnDestroy(): void {
    this.fooDataSubscription.unsubscribe();
  }
}

Running the interval outside of the ngZone prevents protractor from waiting for the polling process to finish, thereby preventing timeouts.

However, this approach has its drawbacks:

  • Unable to use the async pipe; manual subscription management becomes necessary.
  • Manual management and cleanup of subscriptions when the component is destroyed.
  • Loss of the clean and elegant Observable-style, leading to convoluted code - especially with multiple polling services.

The inquiry at hand:

Is there a feasible way to retain the functional rxjs style while still working with the async pipe (or similar) when running the interval outside of the ngZone?

I came across this GitHub project which seems to offer a solution fitting my requirements, but I encountered difficulties implementing it.

I am looking for a practical example of exiting and reentering the zone in my scenario without the need to manually handle subscriptions.

Answer №1

import { Injectable } from '@angular/core';
import { interval, of, timer } from 'rxjs';
import { startWith, switchMap, delay, map, share, shareReplay } from 'rxjs/operators';
import { HttpClient } from '@angular/common/http';

@Injectable()
export class DataService {
  private fakeRequest$ = timer(1500).pipe(map(() => new Date().getTime()))
  private dataPolling$ = interval(3000)
    .pipe(
      switchMap((index) => this.fakeRequest$),
      shareReplay(1)
    )
  constructor() { }

  public fetchData$() {
    return this.dataPolling$;
  }
}

Check out the StackBlitz example

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Retrieve input from text field and showcase in angular 6 with material design components

Take a look at the output image . In the code below, I am displaying the contents of the messages array. How can I achieve the same functionality with a text box and button in an Angular environment? <mat-card class="example-card"> <mat-car ...

Export both the enum and default function in the Typescript declaration for uuidv5

My goal is to create a Typescript Declaration for uuidv5, my first declaration for a 3rd party module. The structure of the module is unfamiliar to me, as it looks like this: function uuidToString(uuid) { } function uuidFromString(uuid) { } function cre ...

Discovering the current date in Angular 8 by utilizing the form builder

Is there a way to automatically fill a form created with FormBuilder with the system's date and time when it is created, instead of the default date format? this.creationDate = moment().format(DATE_TIME_FORMAT); I want to update the creationDate fie ...

What is the correct way to declare a DocumentReference type within an Angular TypeScript interface?

Thank you for taking the time to read my inquiry. Technologies used: Angular 13, Firebase Firestore v9 database, and Firebase Authentication. My question pertains to the process of signing up a user through Firestore Authentication. Upon signup, I need ...

Creating a custom directive in Angular 2 that restricts input to text only

I have recently created a directive that specifically allows only numbers by using key codes. However, I've noticed that when I try to copy and paste text into the text box, it accepts the input but does not display it. Is there a way to utilize &apo ...

Using Fixed Patterns and Combining Types in an Interface

Presently, I am working with this interface: export interface User{ name: string birthday: number | Timestamp ... } When strictTemplates:false is enabled, I have no issue using this interface for server data retrieval with the birthday parameter in ...

Does anyone know of a custom typeguard that enforces the presence of elements in an array?

I need help creating a custom type guard to determine if an array contains nullable or optional elements. I have defined a helper type called RequiredArray: type RequiredArray<A extends readonly any[]> = A extends [infer P, ...infer R] ? [Non ...

What is the process for creating a Deep Copy of an object in Angular?

I have a specific entity class defined as follows: export class Contact { id: number; firstName: string; lastName: string; constructor(id?, firstName?, lastName?) { this.id = id; this.firstName = firstName; this.lastName = lastName; ...

What causes a delay in HTTP calls in Chrome when it is in the "Stalled" or "Initial Connection" state? Is it possible for these states to generate multiple threads for the same request?

My application uses Angular on the client side and Java (Spring Boot) on the backend. Occasionally, during some network calls, the waterfall chart gets stuck in either the "Stalled" or "Initial Connection" state. When this happens, I have noticed in my log ...

Utilize Function type while preserving generics

Is there a way in Typescript to reference a function type with generics without instantiating them, but rather passing them to be instantiated when the function is called? For instance, consider the following type: type FetchPageData<T> = (client : ...

Issues with relocating function during the NgOnInit lifecycle hook in an Angular 2 application

Currently, I am facing an issue with my Angular 2 app where the data sometimes lags in populating, causing a page component to load before the information is ready to display. When this happens, I can manually refresh the page for the data to appear correc ...

The Clerk authMiddleware() function has been defined in the middleware.ts file located at the root of the application, but it is not being utilized

import { authMiddleware } from "@clerk/nextjs"; export default authMiddleware({}); export const config = { matcher: ['/((?!.+\\.[\\w]+$|_next).*)', '/', '/(api|trpc)(.*)&apos ...

Can the detectChanges() method in Angular cause any issues when used with the form control's valueChanges event?

Within my parent Component, I am working with a formGroup and updating its value using patchValue method. ngAfterViewInit() { this.sampleform.controls['a'].patchValue ...} I then pass this form to a child component in the parent component's ...

Combining Rxjs map and filter to extract countries and their corresponding states from a JSON dataset

I have a unique dataset in JSON format that includes information about countries and states. For example: { "countries": [ { "id": 1, "name": "United States" }, { "id": 2, "name": "India" }], "states": [ { ...

Angular Leaflet area selection feature

Having an issue with the leaflet-area-select plugin in my Angular9 project. Whenever I try to call this.map.selectArea, VSCode gives me an error saying that property 'selectArea' does not exist on type 'Map'. How can I resolve this? I& ...

Learn how to set up browser targeting using differential loading in Angular 10, specifically for es2016 or newer versions

Seeking advice on JS target output for compiled Angular when utilizing differential loading. By default, Angular compiles TypeScript down to ES5 and ES2015, with browsers using either depending on their capabilities. In order to stay current, I've b ...

Styling in Angular 2: Leveraging StyleUrls and Custom Styles

I've encountered an issue where I can't use both styleUrls and styles in a Component (whichever is declared last takes precedence). What's the best way to work around this? I'd like to use the ./board.component.css file for base styles ...

Discover the power of Angular's directive microsyntax when working with cdkConnectedOverlays

The Angular docs mention a microsyntax that can be utilized with structural directives. I currently have functional code using the cdkConnectedOverlay directive in its 'long hand' form: <ng-template cdkConnectedOverlay [cdkConnec ...

Guide to showcasing associated information in HTML using Angular 7

I am a beginner with Angular 7 and I am attempting to showcase a product's color on the HTML side using Angular 7 but have not been successful. Below are my tables; Product Id Name Color Id Name ProductColorRelation Id ProductId ColorId In ...

The issue of Three.js Texture not displaying during loading

I'm currently working with Three.js in my Angular Application. I am attempting to display a cup (OBJ file) with a texture applied to it. The issue I am facing is that the texture only appears when I rotate or zoom the object. Otherwise, the object app ...