Issue with Angular project: View not being updated when using behaviorSubjects

Within my Angular project, I am retrieving data from an API using a service and storing the data within a BehaviorSubject as shown below

  private categorySubject = new BehaviorSubject<number | null>(null);
  apiBehavior = new ReplaySubject<ApiResponseInterface>();

  constructor(private http: HttpClient, private authService: AuthService) {}
  getQuestions(page: string = '', category = this.categorySubject.getValue()): Observable<ApiResponseInterface> {
    let url = this.baseUrl;
    if (category) {
      console.log(category);
      url = this.baseUrl + '/category/' + category;
    }
    const params = new HttpParams().set('cursor', page);
    return this.http.get<ApiResponseInterface>(url, { params })
      .pipe(tap((res) => this.apiBehavior.next(res)));
  }

In my component, I am subscribing to the behavior subject like this:

ngOnInit() {
    this.questionsService.getQuestions()
      .pipe(takeUntil(this.destroyed))
      .subscribe((res) => {
        this.apiResponse = res;
        this.questions = res.data;
      });
  }

Everything is working fine up to this point. However, I have another component that sends a new value to the categoryBehaviorSubject within the service using this function:

setCategorySubject(categoryId: number | null) {
    this.questionsService.setCategory(categoryId);
  }

The setCategory function in my service updates the categoryBehaviorSubject with the new value and also updates the apiBehavior like this:

setCategory(categoryId: number | null) {
    this.categorySubject.next(categoryId);
    this.getQuestions();
  }

This is the template of my component which should display questions:

<main
  *ngIf="questions"
  class="p-6 lg:p-20 flex max-md:flex-col gap-6 align-items-center h-full md:justify-between w-full"
>

  <section class="flex flex-col gap-6 align-items-center md:w-4/5 lg:w-2/5">
    <app-category-selector
      class="flex justify-center align-items-center mt-6 md:hidden"
    >
    </app-category-selector>
    <header class="font-primary text-bold text-lg text-center"><h1>{{questions.length}} Posts</h1></header>

    <app-question
      *ngFor="let question of questions"
      [question]="question"
      class="bg-secondary flex p-4 rounded h-1/4"
    ></app-question>
</main>

The issue here is: even though the data is successfully fetched from the API and passed to the apiBehavior Subject, the view is not updated, and new questions are never displayed when a category has been set. What could be causing this discrepancy?

Answer №1

Within my service, the setCategory function sends the new value to the categoryBehaviorSubject and updates the apiBehavior as follows:

setCategory(categoryId: number | null) {
this.categorySubject.next(categoryId);
this.getQuestions(); // does NOT trigger the request
}

You mentioned that it "updates the apiBehavior", however, this is inaccurate. Invoking the getQuestions() function does not prompt a new request; you can monitor your Network tab in the developer tools.

Because another request is not initiated, the code within this block will not be executed either:

return this.http.get<ApiResponseInterface>(url, { params })
  .pipe(tap((res) => this.apiBehavior.next(res)));

Why isn't another request triggered when getQuestions() is called from setCategory?

This is because getQuestions() returns an Observable, and nothing happens until it is subscribed to.

You might argue

But I already subscribed to it in ngOnInit

No, you subscribed to a different Observable there. Once the initial request is completed, the Observable created by the http client is terminated. Subsequently calling getQuestions() generates a new Observable which also requires subscription.

The key point to note is: invoking this.http.get() without subscribing means NO request is made. And no request implies that the pipe operation will not be executed.

If the explanation provided thus far is unclear, I recommend reading the following article for further clarification:

Now, how can you enhance your design?

While I cannot guarantee this is the optimal solution, here is a suggestion:

Create an additional EventEmitter, perhaps named QuestionsObservable (or maybe a BehaviorSubject, depending on your requirements), and push the retrieved data into it every time you fetch the questions.

In the component responsible for displaying the questions, subscribe to QuestionsObservable.

Give it a try, and feel free to reach out if you have any more queries.

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

Expanding and shrinking div elements in Angular with sliding effects on other divs

Hello, I am just starting with angular and angular animations, and I have encountered a problem. Here is the html code that I am working with: <div class="main"> <div class="left" *ngIf="showLeftSide" [@slideInOutLeft]></div> <di ...

Where is the best location to store types/interfaces so that they can be accessed globally throughout the codebase?

I often find myself wondering about the best place to store types and interfaces related to a specific class in TypeScript. There are numerous of them used throughout the code base, and I would rather not constantly import them but have them available gl ...

removing the mapStateToProps function will result in an undefined value

I am new to React and I'm in the process of converting a class component to functional components using hooks. I need some guidance on safely removing 'mapStateToProps' without encountering undefined errors. I have two pages, A.js and B.js. ...

What could be the reason for the absence of Mock Service Worker in a React project that has Typescript enabled?

After attempting to integrate Mock Service Worker into my React project with Typescript support, I encountered errors when running the npm install msw --save-dev command. The terminal displayed the following messages: PS F:\Programming\React Prac ...

Strategies for Creating a Test Suite for RepositoryFactory in Vue.js/Nuxt.js

Summary of RepositoryFactory Implementation An implementation of the RepositoryFactory pattern has been carried out for API connection in a Vue.js/Nuxt.js application. For more details, refer to this article: here hogeRepository.ts import { NuxtAxiosInst ...

Sending data to a parent component from a popup window in Angular Material using a button click while the window is still open

How can I retrieve data from an Angular Material Dialog Box and send it to the Parent component? I am able to access data after the dialog box is closed. However, I am wondering if there is a way to retrieve data while the dialog box is still open, especi ...

Typescript throwing error TS2307 when attempting to deploy a NodeJS app on Heroku platform

Encountering an error when running the command git push heroku master? The build step flags an error, even though locally, using identical NodeJS and NPM versions, no such issue arises. All automated tests pass successfully without any errors. How can this ...

The element type 'HTMLElement' does not contain a property named 'pseudoStyle'

Currently experimenting with adjusting the height of a pseudo element using Typescript. An error is popping up in my IDE (vscode) as I go along. This is the code snippet I am working with. // choose element let el: HTMLElement = document.getElementById( ...

Retrieve data from TypeScript file (.ts) and use it in an HTML document

Recently I started learning Typescript and HTML as I work on building an Angular2 application. At the moment, I have a TypeScript file that resembles the following structure: import {Http, Headers} from 'angular2/http'; import {Component} from & ...

How to Implement Route Resolution for Nested Components in Angular 4?

In my current setup, I have the following hierarchy: Parent Component |__Nested Component 1 |__Nested Component 2 |__Nested Component 3 The challenge I am facing is resolving data into Nested Component 3 since only the Parent Component has a rout ...

Validate uniqueness of input in database using Angular's async validator

My <input> element allows users to enter a value that should be unique in the database. I'm looking for a way to validate this input dynamically on the front-end, and display an error message if the value is already in the database. ...

Set the component variable to hold the output of an asynchronous method within a service

As I work on developing an application, I aim to keep my component code concise and devoid of unnecessary clutter. To achieve this, I plan to offload complex logic into a service which will then be injected into the component. Suppose my component includes ...

Tips for utilizing the JS attribute "offsetWidth" within Angular 4

I am attempting to retrieve the width of an element using JavaScript in my Angular application. document.getElementsByClassName("element")[0].offsetWidth; However, I keep encountering the following compilation error: Property 'offsetWidth' d ...

How can you display a loading indicator after a delay using Observables, but make sure to cancel it if the loading is completed

Within my customer-detail component, I have implemented code that achieves the desired outcome. However, I believe there might be a more reactive and observable way to approach this task. Instead of using an if statement to set this.isLoading = true;, is ...

What is preventing me from uploading the node_modules folder to my GitHub repository?

I've encountered an issue with uploading my Angular 6 project to GitHub using GitHub Desktop. Despite successfully uploading all files, the node_modules file is consistently missing. Upon downloading the project from GitHub and attempting to run it, ...

Position your content on the right side of the Angular Material tabs

I'm struggling with positioning content next to the tabs on the right side. I attempted placing content in a disabled mat-tab, but I don't want the content (located on the right) to be disabled. In addition, the content includes a dropdown menu ...

The Angular route successfully navigated to the page, but the HTML content was not

Whenever I select the Home option in the navigation bar, it takes me to the home URL but doesn't display the HTML content. Below is my app.routing.module.ts code: import { Component, NgModule } from '@angular/core'; import { RouterModule, Ro ...

Error in ag-Grid CSV export feature displaying incorrect file names

Currently, I am coding in Typescript using Angular 2 along with ag-Grid (non-enterprise variant). An issue has arisen with the export feature of ag-Grid and I was wondering if someone could offer some assistance. If there is only one Grid on the form, ex ...

Creating a custom Angular 5 package integrated with external JavaScript libraries

I have developed a custom wrapper for a JavaScript library and I want to distribute it via npm. For this purpose, I am utilizing SystemJS and scriptloader to load the JavaScript library. The setup is working correctly and I am able to successfully build ...

TypeScript will show an error message if it attempts to return a value and instead throws an

Here is the function in question: public getObject(obj:IObjectsCommonJSON): ObjectsCommon { const id = obj.id; this.objectCollector.forEach( object => { if(object.getID() === id){ return object; } }); throw new Erro ...