Why use rxjs observables if they don't respond to updates?

I have an array of items that I turn into an observable using the of function.
I create the observable before populating the array.
However, when the array is finally populated, the callback provided to subscribe does not execute.

As far as I know, the observable only triggers the callback for the items already present in the list, which seems redundant to me.

In a scenario where I use this observable within an *ngFor with the async pipe, it works correctly. But when I pass the observable as a data source to a mat-table or if I provide my own callback to the subscribe function, nothing happens when the list is eventually populated.

What exactly does the async pipe do behind the scenes that I might be overlooking?

export class DiscoveryService {
  private deviceList: DeviceModel[] = [];

  constructor() {
  }

  getDeviceList(): void {
    // Fetch devices from server and add them to the deviceList
  }

  observeDeviceList(): Observable<DeviceModel[]> {
    return of(this.deviceList);
  }
}

export class DeviceListComponent implements OnInit {
  deviceList$: Observable<DeviceModel[]>;

  constructor(private discoveryService: DiscoveryService) { }

  ngOnInit() {
    this.deviceList$ = this.discoveryService.observeDeviceList();

    // The callback here only runs once initially, with an empty list
    this.deviceList$.subscribe(devices => console.log('got devices: ' , devices));

    // After fetching devices from the server, the above subscription callback does not trigger again
    this.discoveryService.getDeviceListx();
  }
}

The async pipe updates correctly, possibly because ngOnInit is called before *ngFor executes. This aspect is still uncertain to me.

<mat-nav-list *ngFor="let device of deviceList$ | async">

Answer №1

Your observable is not responsive to changes because it is created from a static array using the of method, which only emits once. Here is an alternative approach you could take:

DeviceService

export class DeviceService {
  private _deviceList$ = new BehaviorSubject<DeviceModel[]>([]);

  construct() {
    this.fetchDeviceList();
  }

  get deviceList$() {
    return this._deviceList$.asObservable();
  }

  fetchDeviceList() {
     this.http.get<DeviceModel[]>('yourUrl').pipe(
       tap((list: DeviceModel[]) => this._deviceList$.next(list))
     ).subscribe();
  }
}

DeviceListComponent

export class DeviceListComponent implements OnInit {
  private _deviceList$: Observable<DeviceModel[]>;

  constructor(private deviceService: DeviceService) { }

  ngOnInit() {
    this._deviceList$ = this.deviceService.deviceList$;
  }
}

This should allow your template to work properly with the following code:

<mat-nav-list *ngFor="let device of _deviceList$ | async">

Answer №2

export class DeviceService {
  construct(private http: HttpClient) {
  }

  fetchDevices(): Observable<DeviceModel[]> {
     return this.http.get<DeviceModel[]>('api/yourDevices');
  }
}

In case you'd like to implement caching:

export class CachedDeviceService {
  private cachedDevices$: Observable<DeviceList[]>;

  construct(private http: HttpClient) {
  }

  fetchDevices(): Observable<DeviceModel[]> {
     if (!this.cachedDevices$) {
       this.cachedDevices$ = this.http.get<DeviceModel[]>('api/yourDevices').pipe(
         shareReplay(1),
       );
     }

    return this.cachedDevices$;
  }
}

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

What is the best way to utilize the async pipe along with @defer for efficiently loading and declaring variables in the template within Angular 17

One way I can accomplish this is by using @if. An example of this is: @if(items$ | async; as items), where I can assign the array of items to a variable named 'items' using the 'as' keyword in the template. Is there a similar approach ...

Steps to create a clickable row with color change in Angular 4

How do I make an entire row clickable and change the row color when a checkbox is clicked? This is my HTML file: <section class="others"> <div class="sub-header">Others</div> <p class="text-center" *ngIf="otherTests.length === 0"> ...

What is the best way to send props (or a comparable value) to the {children} component within RootLayout when using the app router in Next.js

As I work on updating an e-commerce website's shopping cart to Next.js 13+, I refer back to an old tutorial for guidance. In the previous version of the tutorial, the _app.ts file utilized page routing with the following code snippet: return ( < ...

Passing the value of the attribute from event.target as a parameter in a knockout click event

I have been exploring knockout events and am currently working on a functionality involving three buttons ("Packers", "Trail Blazers", and "Dodgers") within a div. Each button is associated with a data-league attribute of "NFL", "NBA", and "MLB," respectiv ...

Managing the display of numerous ngFor components

If you're interested in learning more about the features I will include, here's a brief overview. I plan to have a project section with cards displayed for each project, all populated from a JSON file. When users click on a card on the website, a ...

Avoid generating file modifications due to a version update when using OpenApiGenerator

When utilizing the typescript-rxjs generator, the issue arises when generating a new version of API clients. The majority of files are altered due to a simple version change: * The version of the OpenAPI document: 1.47.0-rc.20. This results in real change ...

Encountered an issue during the Jest test where the error message states 'Cannot call Class constructor Stack without using the keyword 'new''

I have encountered an issue with my Jest test for an AWS CDK configuration import { expect as expectCDK, matchTemplate, MatchStyle } from '@aws-cdk/assert'; import * as cdk from '@aws-cdk/core'; import { KmsMultiregionPrincipalKey } fro ...

When trying to style a Material UI component in Mui v5, no matches for overloads were found

In my attempt to enhance the style of a Material UI Component (TextField) shown in the image, I encountered an error that seems to persist no matter what troubleshooting steps I take. Interestingly enough, I never faced such issues when working with styled ...

Stop non-logged-in users from accessing page content rendering

Lazy loading is being used in my application to render pages. { path: 'dashboard', loadChildren: './dashboard/dashboard.module#DashboardModule', canActivate: [AuthGuard] } The problem arises when the user types www.mydomain.com/dashbo ...

Leverage Async Await for Setting Response Data in TypeScript

Currently, I am making multiple API requests with different data and storing all the responses in an array. Then, I am using .map to map the response array to my original array to update the data. However, it seems like the process is not working correctly ...

Integrating a fresh element into the carousel structure will automatically generate a new row within Angular

I'm currently working on an Angular4 application that features a carousel displaying products, their names, and prices. At the moment, there are 6 products organized into two rows of 3 each. The carousel includes buttons to navigate left or right to d ...

Problem with rendering React Router v4 ConnectedRouter on nested routes

The routes for the first level are correctly displayed from Layout.tsx, but when clicked on ResourcesUI.tsx, the content is not rendered as expected (see code below). The ResourceUI component consists of 2 sections. The left section contains links, and th ...

Tips for integrating a custom handler to the close icon in Material UI TextField component

In my Reactjs/Typescript project using Material UI, I have a search input component rendered with TextField. The built-in "x" icon clears the input value, but I want to create a custom handler for making an API call when the search value is deleted. I&apo ...

Searching for two variables in an API using TypeScript pipes

I'm stuck and can't seem to figure out how to pass 2 variables using the approach I have, which involves some rxjs. The issue lies with my search functionality for a navigation app where users input 'from' and 'to' locations i ...

What causes the inconsistency in TypeScript's structure typing?

It is well-known that TypeScript applies structure typing, as demonstrated in the following example: interface Vector { x: number; y: number; } interface NamedVector { x: number; y: number; name: string; } function calculateLength(v: Vecto ...

Specify the return type based on specific parameter value

I'm facing a situation where I have two definitions that are identical, but I need them to behave differently based on the value of the limit parameter. Specifically, I want the first definition to return Promise<Cursor<T>> when limit is g ...

declaration of function interface and property that cannot be modified

After reviewing some new TypeScript code, I encountered a part that left me puzzled. interface test { (a: number): number; readonly b: number; } While I understand that (a:number): number signifies a function where the argument is a:number and the ret ...

The Typescript compiler will continue to generate JavaScript code even if there are compilation errors

As a fresh learner of TypeScript, I have been experimenting with some basic concepts. Below is the code from my file app1.ts: class Monster { constructor(name, initialPosition) { this.name = name; this.initialPosition = initialPosition ...

The absence of essential DOM types in a TypeScript project is causing issues

Recently, I've been working on setting up a web app in TypeScript but I seem to be missing some essential types that are required. Every time I compile using npm run build, it keeps throwing errors like: Error TS2304: Cannot find name 'HTMLEleme ...

Troubleshoot: ng-bootstrap Carousel Functionality Issue

While testing the various components on ng-bootstrap, I encountered an issue with getting the carousel to work. Strangely enough, all the other ng-bootstrap components are functioning perfectly. Upon implementing the code from , the result is a blank white ...