Problem with RxJS array observable functionality not meeting expectations

I am struggling with an Angular service that contains an array of custom objects and an observable of this array. An external component subscribes to the observable but does not respond when the list is modified. Additionally, the mat-table, which uses the observable as its data source, also does not update.

The service utilizes a gRPC stream to fetch data:

export class ScanService {
    private scanClient: ScanClient = new ScanClient('http://' + window.location.hostname + ':8080', null, null);
    private results: ItemModel[] = [];
    readonly results$ = from(this.results);

    constructor() { }

    start(ip: string): boolean {
        let stream = this.scanClient.listen(ip, {});

        stream.on('data', (response) => {
            this.results.push({
                name: response.getName(),
                type: response.getType(),
            });

            console.log('stream got item: ' + response.getName());
        });
    }
}

The consumer:

export class ScanComponent implements OnInit {
    //...
    results$: Observable<ItemModel[]>;

    constructor(private scanService: ScanService) { }

    ngOnInit() {
        this.results$ = this.scanService.results$;
        this.results$.subscribe({next: function(results){console.log('subscriber got item: ', results);}});
    }

    onScan() {
        this.scanService.start(this.ip);
    }
}
<table mat-table [dataSource]="results$" class="mat-elevation-z8">

In the console output, I can see "stream got item" for each item, but there is no "subscriber got item" message, and the mat-table remains empty.

EDIT:

I have another example using `of` that works correctly. The only difference is that the gRPC call returns an object directly instead of a stream.

Service:

export class DiscoveryService {
     private deviceList: DeviceModel[] = [];
     private discoveryClient: DiscoveryClient = new DiscoveryClient('http://' + window.location.hostname + ':8080', null, null);

     constructor() {}

     getDeviceList(): void {
        this.discoveryClient.devices(new EmptyProto(), {}, (err, reply) => {
            const pbDeviceList = reply.getDevicesList();
            for (const pbDevice of pbDeviceList) {
                this.deviceList.push({ ip: pbDevice.getIp()});
            }
        });
     }

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

Consumer:

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

   constructor(private discoveryService: DiscoveryService) { }

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

       this.discoveryService.getDeviceList();
   }
}
 <mat-nav-list *ngFor="let device of deviceList$ | async">

Answer №1

In addition to the valuable input from other commenters, let me summarize the key points for you:

You're currently dealing with two main issues:

of() vs from()

You can find more information on this topic in this post. Essentially, using of([1,2,3]) creates an observable that emits "[1,2,3]" as an array all at once, while from([1,2,3]) emits "1", then "2", and finally "3" individually. Based on how you are using result$, it seems like you should be utilizing of().

Subject vs Observable

If you want updates to your result array to be broadcasted to subscribers of the result$ observable, you need to consider that Observables complete after emitting all their values, which automatically unsubscribes any subscribers after emitting the initial static data present at subscription time. To address this, consider using Subjects, particularly BehaviorSubjects, instead of Observables. These act as publishers and emit the last value to new subscribers. Additionally, you can push new data to subscribers by calling their next() method.

const result$ = new BehaviourSubject<ItemModel[]>(this.result);

This type allows you to update subscribers with new content using

result$.next(newValue);

instead of directly assigning a new value to result like so:

result = newValue;

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

Guidelines for establishing authentic headers on a SignalR connection

Can headers be set on the SignalR connection directly? I am aware of setting query string parameters but it is not secure enough for my specific scenario. var conn = ($ as any).hubConnection(); conn.url = URL; conn.qs = { "token": SECRET_KEY }; conn ...

Issue with mui TextField label width not adjusting properly with font override

Whenever I change the font of the label, the width of the label does not adjust accordingly and the text appears to be outlined. For a demonstration, you can check out this example on CodeSandbox ...

Deduce the types of parameters in nested functions based on the parent object

Having encountered a challenging obstacle in my Typescript journey, I find myself facing a dilemma with building a command-parsing utility. My aim is to incorporate type hints within configuration objects. Let's delve into the code example below; int ...

how to retain the state value as a number and enable decimal input in a TextField component in React

Currently working on a project using React, Material UI, and TypeScript. I need to enable decimal values in a TextField while ensuring that my state value remains a number type instead of a string. export default function BasicTextFields() { const [value ...

Altering the parent component's output depending on a boolean value established in the child component within Angular

Recently I began learning Angular and find myself in need of some assistance when it comes to managing a specific situation with Angular 16. In our project, we have two different versions of the site header represented by two components - one as the defaul ...

Validation scheme for the <speak> element

When using validators in an angular formarray for input fields, I encountered a challenge with the regex to check the <speak> tag. The content provided was considered valid. An error is thrown based on the specified pattern. However, it should als ...

In TypeScript, if all the keys in an interface are optional, then not reporting an error when an unexpected field is provided

Why doesn't TypeScript report an error when an unexpected field is provided in an interface where all keys are optional? Here is the code snippet: // This is an interface which all the key is optional interface AxiosRequestConfig { url?: string; m ...

Identify any missing periods and combine the years into a single range

I am working on restructuring year ranges with gaps and consolidating them. For example, converting [{start: 2002, end: 2020}, {start: 2020, end: null}] to {start: 2002, end: null} or [{2002, 2004},{2006, 2008}, {2008, null}] to [{2002-2004}, {2006-null}]. ...

Challenges with Spreading Props in TextField Component After MUIv4 Upgrade with TypeScript

Latest Material-UI Version: 4.1.0 I'm encountering difficulties in passing props to an abstracted <TextField /> component that I've developed. Below is the snippet of code: PasswordInput.tsx import * as React from 'react' impo ...

How can the '!!user' syntax be utilized? What outcome does this code snippet produce?

I am looking to implement an angular route guard in my application. I came across this code snippet but I am confused about the line where user is mapped to !!user. Can someone explain the purpose of map(user => !!user) in this context? canActivate( ...

What is the best way to construct an interface in TypeScript with a variable number of properties?

Is it possible to create an interface in typescript with a variable number of string properties, ranging from 5 to potentially 50? ...

Difficulty in activating or deactivating form controls in Angular 2 upon receiving an HTTP response

When using formcontrol in Angular, I encountered an issue where I tried to disable the form control based on a variable assigned from an HTTP response. Angular2 gave me a warning message. The warning mentioned that it's not recommended to use the dis ...

having difficulties connecting the paginator with MatTable

I'm struggling to implement pagination for my mat-table in Angular 6. I've referenced some examples from the following link, but unfortunately, it's not functioning as expected: https://material.angular.io/components/table/examples While t ...

Incorporating Interpolation into Angular 2 Routing

I have a dropdown menu for selecting categories, and based on the selected category, I want to update the router link for the menus. For example, if I select Category1, then the router link for SubMenu1 should be BASE/category1/submenu1. Similarly, for Cat ...

Using props in React can be declared either as a `type` or an `interface`

I am working with React code export default class MyComponent extends Component<Props,State> I'm trying to figure out whether I should define my props using a type or an interface. type Props = { isActive: Boolean, onClick: Function } ...

How to apply CSS classes to dynamically injected HTML in Angular 7

One of the challenges I'm currently facing is how to assign a CSS class based on a calculation in my component. Here's a snippet from my component class: @Component({ selector: 'app-render-json', template: `<div [innerHtml ...

Defining assertions with specified type criteria

Looking to do something special in TypeScript with a class called Foo. I want to create a static array named bar using const assertion, where the values are restricted to the keys of Foo. This array will serve as the type for the function func while also a ...

Combining Axios with repeated promises

I am facing an issue with a loop in my GET request on the axis, and I cannot figure out why. const [ state, setState ] = useState<any[]>([]); ids.forEach((id) => { getData(id) .then((smth: Map<string, any>[]) => getNeededData ...

Struggling to integrate D3.js with React using the useRef hook. Any suggestions on the proper approach?

I'm currently working on creating a line chart using d3.js and integrating it into React as a functional component with hooks. My approach involved utilizing useRef to initialize the elements as null and then setting them in the JSX. However, I encou ...

The submit button seems to be unresponsive or unreactive

As a newcomer to Angular 2 and Typescript, I am in the process of building a web application. I have created several input fields and, following user submission via a button, I want to log the inputs to the console. However, it seems like my button is not ...