poll

The poll operator is a custom RxJS operator that simplifies the process of polling APIs to keep data up-to-date. It allows developers to create observables that periodically emit data at specified intervals, abstracting away the complexity of managing polling intervals and API requests. This operator is especially useful when real-time updates are required but the server does not support push notifications (WebSockets, Server-Sent Events, etc.). It seamlessly integrates with other RxJS operators, enabling developers to build reactive pipelines for handling data streams.

Examples

Example 1: Basic Polling

Poll data at a specified interval. The polling function supports Promise, Observable, or primitive values:

import {poll} from 'ngx-lift';
import {ajax} from 'rxjs/ajax';

// Return a primitive value
poll({
  interval: 5000, // Poll every 5 seconds
  pollingFn: () => Math.random(),
}).subscribe(console.log);

// Return a Promise
poll({
  interval: 5000,
  pollingFn: () =>
    new Promise((resolve) => {
      setTimeout(() => {
        resolve(Math.random());
      }, 200);
    }),
}).subscribe(console.log);

// Return an Observable
poll({
  interval: 5000,
  pollingFn: () => ajax('https://api.example.com/data'),
}).subscribe(console.log);

Example 2: Poll Until Condition is Met

Use RxJS operators like takeWhile to stop polling when a condition is met:

import {poll} from 'ngx-lift';
import {of, takeWhile, delay} from 'rxjs';

poll({
  interval: 1000,
  pollingFn: () => of(Math.random() * 10).pipe(delay(300)),
})
  .pipe(takeWhile((state) => state.data === null || state.data <= 8, true))
  .subscribe(console.log);

Example 3: Poll with initialValue

By default, the poll initial result is {status: 'loading', isLoading: true, error: null, data: null}, which is convenient for displaying a loading spinner. However, in some cases, you may not want to display a loading spinner. You can set initialValue to any value you want:

import {poll} from 'ngx-lift';
import {of, delay} from 'rxjs';

poll({
  interval: 1000,
  pollingFn: () => of(Math.random() * 10).pipe(delay(300)),
  initialValue: {status: 'idle', isLoading: false, error: null, data: 0}, // display 0 for initial value
}).subscribe(console.log);

Example 4: Defer Polling

By default, polling starts immediately. Use the delay option to delay the start of polling:

import {poll} from 'ngx-lift';
import {of} from 'rxjs';

poll({
  interval: 1000,
  pollingFn: () => of(Math.random() * 10),
  delay: 3000, // delay 3s and start polling
}).subscribe(console.log);

Alternatively, you can start polling after a user action by combining poll with other RxJS operators:

import {poll} from 'ngx-lift';
import {Subject} from 'rxjs';
import {delay, of, switchMap, takeWhile} from 'rxjs';

const startPolling = new Subject<void>();

// start button click
function start() {
  startPolling.next();
}

startPolling
  .pipe(
    switchMap(() => {
      return poll({
        interval: 1000,
        pollingFn: () => of(Math.random() * 10).pipe(delay(300)),
      }).pipe(takeWhile((state) => state.data === null || state.data <= 8, true));
    }),
  )
  .subscribe(console.log);

Example 5: Force Refresh

Use the forceRefresh observable or signal to trigger the execution of pollingFn whenever necessary, such as in response to user actions. The emitted value from forceRefresh will be passed as the input to paramsBuilder (if provided) or directly to pollingFn.

import {poll} from 'ngx-lift';
import {Subject} from 'rxjs';

export class MyComponent {
  private refresh$ = new Subject<void>();

  dataState$ = poll({
    interval: 5000,
    pollingFn: () => this.http.get('/api/data'),
    forceRefresh: this.refresh$,
  });

  refresh() {
    this.refresh$.next();
  }
}

Example 6: Advanced Example - Datagrid with Polling

The datagrid below will automatically refresh every 10 seconds. Additionally, whenever there's a change in the input, triggering filtering, sorting, or pagination, the API will be promptly called, accompanied by a spinner indicating loading.

Single selection header
Use left or right key to resize the column
Use left or right key to resize the column
Use left or right key to resize the column
Use left or right key to resize the column
 / 10
import {ClarityModule, ClrDatagridStateInterface} from '@clr/angular';
import {AlertComponent, convertToHttpParams, dgState} from 'clr-lift';
import {poll} from 'ngx-lift';
import {inject} from '@angular/core';
import {BehaviorSubject} from 'rxjs';

export class PollComponent {
  userService = inject(UserService);

  private dgBS = new BehaviorSubject<ClrDatagridStateInterface | null>(null);
  private dgState$ = this.dgBS.pipe(dgState(false));

  usersState$ = poll({
    interval: 10_000,
    pollingFn: (params) => this.userService.getUsers({...params, results: 10, seed: 'abc'}),
    paramsBuilder: (dgState: ClrDatagridStateInterface | null) => convertToHttpParams(dgState), // build params for getUsers
    forceRefresh: this.dgState$,  // datagrid filter, sort, pagination change will immediately force a refresh of the API call
  });
}

API Reference

poll

Polls data at a specified interval and can be triggered manually. Returns an observable that emits the result as an AsyncState object.

Signature

poll<Data, Input>(options: PollOptions<Data, Input>): Observable<AsyncState<Data>>

interface PollOptions<Data, Input> {
  pollingFn: (input?: Input) => Observable<Data> | Promise<Data> | Data;
  interval: number;
  forceRefresh?: Observable<Input> | Signal<Input>;
  paramsBuilder?: (input: Input) => any;
  initialValue?: AsyncState<Data>;
  delay?: number;
}

Parameters

  • pollingFn: (input?: Input) => Observable<Data> | Promise<Data> | Data

    Required. A function that returns an Observable, Promise, or primitive value. The function receives parameters built from forceRefresh or paramsBuilder.

  • interval: number

    Required. The interval in milliseconds between each poll.

  • forceRefresh?: Observable<Input> | Signal<Input>

    (Optional) An Observable or Signal that triggers a manual refresh. The emitted value is passed to paramsBuilder (if provided) or directly to pollingFn.

  • paramsBuilder?: (input: Input) => any

    (Optional) A function that builds parameters for the polling function based on the forceRefresh value. Useful when the forceRefresh value does not match the structure of pollingFn parameters.

  • initialValue?: AsyncState<Data>

    (Optional) An initial AsyncState value. Defaults to { status: 'loading', isLoading: true, error: null, data: null }.

  • delay?: number

    (Optional) Delay in milliseconds before starting the first poll. Defaults to 0 (starts immediately).

Returns

An observable that emits AsyncState<Data> objects with status, isLoading, error, and data properties.

  • status: ResourceStatus

    New! Granular status tracking: 'idle' | 'loading' | 'reloading' | 'resolved' | 'error'

  • isLoading: boolean

    Recommended! Indicates if any loading operation is in progress. Returns true when status === 'loading' || status === 'reloading'.

  • isLoading: boolean

    Deprecated Use isLoading instead.

  • error: E | null

    Encountered errors during the operation.

  • data: T | null

    Successful result of the operation.