connecting and linking template content with an Observable

I have a CRUD page that needs to be updated after every operation.

I have implemented Observable and the CRUD functions (specifically Add and Delete) are working fine, but I need to manually refresh the page to see the changes reflected.

After trying to call this.ngOnInit() at the end of each operation function, I still couldn't get it to work as expected.

productmanagement.component.ts

import { Component, OnInit } from '@angular/core';
import { routerTransition } from '../../../router.animations';
import { Observable } from 'rxjs';
import { Category } from 'src/app/_models';
import { CategoryService } from './_services/category.service';

@Component({
    selector: 'app-productmanagement',
    templateUrl: './productmanagement.component.html',
    styleUrls: ['./productmanagement.component.scss'],
    animations: [routerTransition()],
    providers: [CategoryService]
})
export class ProductManagementComponent implements OnInit {

    public categoriesObservable$: Observable<Category>;

    constructor(private categoryService: CategoryService) {}

    ngOnInit() {
        this.categoriesObservable$ = this.categoryService.getAll();
    }

    categoryAdded(categoryname: string) {
        this.categoryService.add(categoryname);

    }

    deleteCategory(category: any) {
        this.categoryService.delete(category.idCategory);
    }
}

category.service.ts

@Injectable()
export class CategoryService {
    public categoriesRequest: Observable<Category>;
    public categorySubject: Subject<Category>;

    constructor(private http: HttpClient) {
        this.categorySubject = new ReplaySubject(1);
    }

    getAll(refresh: boolean = false) {
        if (refresh || !this.categoriesRequest) {
            this.categoriesRequest = this.http.get<Category>(`http://localhost:5000/api/Category`);

            this.categoriesRequest.subscribe(
                result => this.categorySubject.next(result),
                err => this.categorySubject.error(err)
              );
        }
        return this.categorySubject.asObservable();
    }

    getById(id: number) {
         return this.http.get(`http://localhost:5000/api/Category/` + id);
    }

    add(category: string) {
        return this.http.post<Category>('http://localhost:5000/api/Category', {Category: category}).subscribe();
    }

    update(category: Category) {
        return this.http.put(`http://localhost:5000/api/Category/` + category.idCategory, category);
    }

    delete(id: number) {
        return this.http.delete<any>(`http://localhost:5000/api/Category?id=` + id).subscribe();
    }
}

productmanagement.component.html

<!--Categories-->
    <div *ngIf="(categoriesObservable$ | async)">
        <div *ngFor="let category of (categoriesObservable$ | async)">
            <div class="card">
                <div class="card-header">
                    <p style="display: inline" name='idCategory'> {{ category.category1 }} </p>

                    <button class='btn btn-danger' style="float: right;" (click)="deleteCategory(category)">delete</button>
                    <button class='btn btn-warning' style="float: right; margin-right: 10px;">edit</button>
                    <button class="btn btn-success" style="float: right; margin-right: 10px;">add Product</button>
                </div>
                <div *ngIf="category.product.length>0 else noProduct">
                    <table class="card-body table table-hover">
                        <thead>
                            <tr>
                                <th>Product</th>
                                <th>Reference</th>
                                <th>Serial Number</th>
                                <th>Price</th>
                                <th>Quantity</th>
                                <th style="width: 165px"></th>
                            </tr>
                        </thead>
                        <tbody *ngFor="let prod of category.product">
                            <tr>
                                <td>{{ prod.product1 }}</td>
                                <td>{{ prod.reference }}</td>
                                <td>{{ prod.serialNumber }}</td>
                                <td>{{ prod.price }}</td>
                                <td>{{ prod.quantity }}</td>
                                <td>
                                    <button class='btn btn-outline-warning'>edit</button> &nbsp;
                                    <button class='btn btn-outline-danger'>delete</button>
                                </td>
                            </tr>
                        </tbody>
                    </table>
                </div>
                <ng-template #noProduct class="align-content-center">
                    <br>
                    <p class='text-center'>No Products yet</p>
                </ng-template>
            </div>
            <br>
        </div>
    </div>

Even with including this.ngOnInit() in the component and trying the AsyncPipe in the template, I still faced issues. Thus, binding the Observable with the template might offer the solution.

Answer №1

Why make things more complicated than they need to be? Creating another Observable is unnecessary when "http's" get() already returns an Observable. Simply unsubscribe from the subscription when your component is destroyed. The purpose of adding the if() block in the getAll() method is to avoid making another HTTP request if one is already in progress.

Redefine your getAll() method like this:

getAll() {
    this.http.get<Category>(`http://localhost:5000/api/Category`);
}

Create a class variable in your component to hold the subscription:

getAllSubscription: Subscription

Add a getAllCategories() method in your component.

Instead of using the categorysObservable$ property, use a categories property to store the actual data.

getAllCategories() {
  if (this.getAllSubscription) {
    this.getAllSubscription.unsubscribe();
  }
  this.getAllSubscription = this.categoryService.getAll().subscribe((data) => {
    this.categories = data;
  }, (error) => {
      // handle errors here
  })
}

Remove the async pipe from your template and replace it with:

<div *ngIf="categories">
    ....
</div>

In your service, modify other methods to only return Observables without subscribing, for example:

add(category: string) {
    return this.http.post<Category>('http://localhost:5000/api/Category', {Category: category});
}

Call getAllCategories() in your categoryAdded() and deleteCategory() methods.

categoryAdded(categoryname: string) {
    this.categoryService.add(categoryname).subscribe((data) => {
       // do something with data, display error or success messages, etc.
       this.getAllCategories();
    })
}

Note: There are alternative ways (using rxjs flattening operators) to achieve a getAll() directly in update() and delete() Observables themselves. I have separated them into individual Observables for distinct success/error responses from each request.

Update:

If you prefer to use flattening operators, you can implement them as follows:

add(category: string) {
    return this.http.post<Category>('http://localhost:5000/api/Category', {Category: category}).pipe(
      mergeMap((dataFromAdd) => {
        // you can simply return this.getAll() if you don't require response from the above post() request. (But it's important!)
        return this.getAll().pipe(
          map((dataFromGetAll) => {
                return {
                  addResponse: dataFromAdd,
                  allCategories: dataFromGetAll
                }
            })
          )
      })
    )
}

In your component, adjust categoryAdded() method as shown below:

categoryAdded(categoryname: string) {
    this.categoryService.add(categoryname).subscribe((data) => {
       this.categories = data.allCategories
    })
}

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

Unlocking Column Data Tooltips in Angular Datatables: A Step-by-Step Guide

I have a single datatable and was wondering how to implement tooltips for when hovering over table cells. I tried the following code snippet, which successfully populated the tooltips. However, I am interested in achieving the same functionality using Angu ...

Securing Your Next.js Web App with Session Authentication

I have encountered a challenge while integrating NextAuth authentication into my React.js web application. To ensure seamless user authentication across the entire app, I am attempting to wrap a SessionProvider around the root component. However, VSCode ...

Issue with NullInjectorError: Failure to provide a custom component - Attempting to add to providers without success

I keep encountering errors during my test attempts... Despite looking into similar issues, I am still facing problems even after adding my custom LoginComponent to app.module.ts providers. It is already included in the imports section. app.module.ts @Ng ...

Transform an Array into a String using Angular

Is there a more efficient way to extract all the state names from the array (testArray) and convert them to strings ('Arizona','Alaska','Florida','Hawaii','Gujarat','Goa','Punjab'...)? ...

Struggling to integrate the Animejs library into my TypeScript and webpack project

Transitioning from ES5 to TypeScript and webpack, I decided to incorporate the Three.js library seamlessly. Alongside this, I wanted to utilize the Anime.js library for its impressive timeline animation features. However, my efforts yesterday were fraught ...

What could be causing my Angular project to not run properly without any changes made after creating a new component that I want to include in app.component.html?

Greetings, I am just starting out with Angular 17 and I am currently going through the Tour of Heroes course on the official website. Following the tutorial's instructions, I created a new component called 'heroes' after setting up my projec ...

Obtain accurate dispatch type by leveraging ConnectedProps in conjunction with redux-thunk

Currently, I am utilizing Redux-Toolkit and Typescript. Specifically, my goal is to implement ConnectedProps as recommended in the redux documentation. However, it appears that the dispatch type is not recognized correctly (it is identified as a normal Dis ...

Defining a custom type for accessing Date.getTime() in TypeScript

Are there any data types similar to Timestamp that could be utilized for Date().getTime() purposes? const currentTime = new Date().getTime(); ...

Exploring the representation of recursive types using generic type constraints

Is there a way to create a structure that can handle recursive relationships like the one described below? I am looking to limit the types of values that can be added to a general container to either primitive data types or other containers. Due to limit ...

Using Kendo PanelBarItem to incorporate a personalized component as a child element

I was looking to design a component with PanelBarItems in its template. However, I'm facing issues and it doesn't seem to be working. Any suggestions? Main Component: import { Component } from '@angular/core'; @Component({ selecto ...

Optimizing Your Approach for Rolling Out Test and Development Application Versions on Google Compute Platform

Google Computing Platform I currently have an Angular (2) application and a Node.js middleware (Loopback) running as Services in an App Engine within my project. We are using a Compute Engine to run PostgreSQL for our database in the same project. Our Go ...

Transitioning from Angular 8 to Angular 9 - troubleshooting hurdles

After diligently following the official documentation to update my Angular app, I encountered several errors when running ng serve. ERROR in app/src/app/users/add/add.component.html:14:48 - error NG2345: Argument of type 'AbstractControl' is not ...

Why doesn't Typescript 5.0.3 throw an error for incompatible generic parameters in these types?

------------- Prompt and background (Not crucial for understanding the question)---------------- In my TypeScript application, I work with objects that have references to other objects. These references can be either filled or empty, as illustrated below: ...

Rearrange list items by dragging and dropping

Here is the HTML and TypeScript code I have implemented for dragging and dropping list items from one div to another: HTML: <div class="listArea"> <h4> Drag and Drop List in Green Area: </h4> <ul class="unstyle"> <l ...

Typescript: Subscribed information mysteriously disappeared

[ Voting to avoid putting everything inside ngOnit because I need to reuse the API response and model array in multiple functions. Need a way to reuse without cluttering up ngOnInit. I could simply call subscribe repeatedly in each function to solve the p ...

How to retrieve a value from an Angular form control in an HTML file

I have a button that toggles between map view and list view <ion-content> <ion-segment #viewController (ionChange)="changeViewState($event)"> <ion-segment-button value="map"> <ion-label>Map</ion-label> & ...

Issue occurred when trying to load controllers during the migration process from AngularJS1 to Angular6

Currently, I am in the process of upgrading AngularJS1 components to Angular6. My strategy involves creating wrappers for all existing AngularJS1 components by extending "UpgradeComponent" and storing them under the folder "directive-wrappers". However, wh ...

Angular recognizing string-array type as a string input is not correct

I am encountering a challenge with an Angular CLI component that involves working with an array of strings called "searchResult": export class ParentComponent implements OnInit { mockArray: string[] = []; searchString: string = ''; searchR ...

Zone.js error: Promise rejection caught

When I call a function from an external library on page load in my Angular application, Chrome dev tools console shows the error message: "Unhandled Promise rejection: Cannot read properties of undefined (reading 'page') ' Zone: <root> ...

Warning issued by Angular 7 indicating the usage of the routerLink directive and advising against navigation triggered outside the Angular zone

Encountering difficulties while working with the Angular framework to ensure smooth functioning of my application, I am currently facing an obstacle with routing. The structure includes a main AppComponent and a app-routing.module.ts file for navigation ma ...