Home > Enterprise >  Angular - Subscribing to data from the service instead of the component
Angular - Subscribing to data from the service instead of the component

Time:01-13

I have a simple Angular 2 app that is working but I'd like to move the subscription logic to the service instead of the component. Open to any other improvements as well.

CURRENT

app.component.ts

import { Component } from '@angular/core';
import { Product, AppService } from './app.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'Product Test';
  products: Product[] = [];

  constructor(private appService: AppService) {
    this.loadData();
  }

  loadData(): void {
    this.appService.getProducts().subscribe(
      (results) => {
        //console.log(results);
        this.products = results;
      },
      (err) => {
        console.log(err);
        //add to alert logger
      }
    );
  }
}

app.service.ts

import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class AppService {
  dataProducts: Product[] = [];

  constructor(private http: HttpClient) {}

  getProducts(): Observable<Product[]> {
    return this.http.get<Product[]>('../assets/products.json');
  }
}

export type Product = {
  ProductID: number;
  ProductName: string;
};

What I'd like but doesn't return data although it doesn't have any errors.

app.component.ts

import { Component } from '@angular/core';
import { Product, AppService } from './app.service';

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  title = 'Product Test';
  dataProducts: Product[] = [];

  constructor(private appService: AppService) {
    this.loadData();
  }

  loadData(): void {
    this.dataProducts = this.appService.dataProducts;
  }
}

app.service.ts

import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';

@Injectable({
  providedIn: 'root',
})
export class AppService {
  dataProducts: Product[] = [];

  constructor(private http: HttpClient) {
    this.http.get<Product[]>('../assets/products.json').subscribe(
      (results) => {
        //console.log(results);
        this.dataProducts = results;
      },
      (err) => {
        console.log(err);
        //add to alert logger
      }
    );
  }
}

export type Product = {
  ProductID: number;
  ProductName: string;
};

CodePudding user response:

You are going to have to subscribe in the component somewhere or else it'll never know when to get the data (when the request is fulfilled). If you want to store the data in the service (multiple components want the same data and it may update) you can use a BehaviorSubject. This will allow you to subscribe to any changes on the variable. Then the second option should work. That would still require you to subscribe in the component though which is what you want to avoid and would be unnecessary from what you've described but still an option.

The normal solution would be to use a pipe. This way you can store the data in the service as well as return the value. That would look like

// app.service.ts

getProducts(): Observable<Product[]> {
    return this.http.get<Product[]>('../assets/products.json').pipe(
        tap((products: Product[]) => this.dataProducts = products),
        catchError((err: any) => console.log(err)),
    );
}

and then in app.component.ts

// app.component.ts

loadData(): void {
  this.appService.getProducts().subscribe(
    (products: Product[]) => this.products = products,
  );
}

Notice that the error is already taken care of in the service. If you want to do something else you can always do stuff both places. You can find a reference for tap, catchError, and more here, but the main ones you should learn are map, tap, catchError, mergeMap.

For another improvement, I find it much better to have a models/ folder that contains all custom classes and interfaces. That way it is easy for everybody to know what a Product is (there's no reason to type if only one part knows the type).

  •  Tags:  
  • Related