Service

As we have discussed in Angular Architecture, Services are used for reusable data to share between components throughout an application.

Services are mainly used for HTTP Calls.

Components shouldn’t fetch or save data directly and they certainly shouldn’t knowingly present fake data. They should focus on presenting data and delegate data access to a service.

In the previous chapter, we have created two component, ProductsComponent and ProductComponent. We are getting the dummy product list data from the MockData class.

We have also written the code to remove product in component. If we required this code in other component, we need to write it again.

It is not a good practice, to write business logic in component.

Instead of writing business logic code on component, we will move this common reusable code into Service class.

As shown below, we will create a separate class called UserService, which will contain the reusable code and code to call the HTTP web service.

Methods written in service can be used by any component just by writing a dependency in that component constructor (dependency injection).

Services with Dependency Injection

We will create all services in a separate folder called service. It is not a mandatory, but it is a good way to manage services and components separately.

Using terminal go into service folder and execute below Angular CLI command.

ng g service product

The command generates skeleton ProductService class in src/app/service/product.service.ts. The ProductService class should look like the below.

import { Injectable } from '@angular/core';

@Injectable()
export class ProductService {

  constructor() { }

}

Notice that the new service imports the Angular Injectable symbol and annotates the class with the @Injectable() decorator.

The @Injectable() decorator tells Angular that this service might itself have injected dependencies.

Service could get data from anywhere—a web service, local storage, or a mock data source.

Removing data access from components means you can change your mind about the implementation anytime, without touching any components. They don’t know how the service works.

The implementation in this tutorial will continue to deliver mock heroes.

We will update the ProductService as below, 

import { MockData } from './../mock-data/mock-product-data';
import { Injectable } from '@angular/core';
import { Product } from '../models/product';

@Injectable()
export class ProductService {

  products: Product[] = [];
  constructor() {
    this.products = MockData.Products;
  }

  getProducts(): Product[] {
    return this.products;
  }

  removeProduct(product: Product) {
    let index = this.products.indexOf(product);
    if (index !== -1) {
      this.products.splice(index, 1);
    }
  }
}

We must provide the ProductService in the dependency injection system before Angular can inject it into the ProductsComponent and ProductComponent, as we will do below.

There are several ways to provide the ProductService: in the ProductsComponent, in the AppComponent, in the AppModule. Each option has pros and cons.

In this tutorial, we will provide ProductService in AppModule.

That’s such a popular choice that we could have told the CLI to provide it there automatically by appending --module=app.

ng g service product --module=app

Since we did not, we will have to provide it manually.

Open the AppModule class, import the ProductService, and add it to the @NgModule.providers array.

import { ProductService } from './service/product.service';
@NgModule({
        ...
  	providers: [ProductService],
  	...	
})
export class AppModule { }

The providers array tells Angular to create a single, shared instance of ProductService and inject into any class that asks for it.

The ProductService is now ready to plug into the ProductsComponent and ProductComponent.

Update ProductsComponent as below, Delete the MockData import as we won’t need that anymore.

import { ProductService } from './../service/product.service';
import { Component, OnInit } from '@angular/core';
import { Product } from '../models/product';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {

  products: Product[] = [];

  constructor(public productService: ProductService) {
    this.products = productService.getProducts();
  }

  ngOnInit() {
  }

  deleteProduct(product: Product) {
    this.productService.removeProduct(product);

    this.products = this.productService.getProducts();
  }

}

as shown above, we have injected ProductService using a constructor. 

Now, we can use the ProductService methods into ProductsComponent. we have used the getProducts() and removeProduct() method.

If you will not provide the ProductService in containing module or in that component. it will generate an error as below : 

As you can see in the error, a component is not able to find the provider of ProductService. Because we have not provided ProductService in the provider property of @NgModule metadata.

To resolve this error, add ProductService in the provider array of containing module, here containing module is AppModule.

import { MockData } from './../mock-data/mock-product-data';
import { Injectable } from '@angular/core';
import { Product } from '../models/product';

@Injectable()
export class ProductService {

  products: Product[] = [];
  constructor() {
    this.products = MockData.Products;
  }

  getProducts(): Product[] {
    return this.products;
  }

  removeProduct(product: Product) {
    let index = this.products.indexOf(product);
    if (index !== -1) {
      this.products.splice(index, 1);
    }
  }
}
import { ProductService } from './service/product.service';
import { BrowserModule } from '@angular/platform-browser';
import { NgModule } from '@angular/core';
import { AppComponent } from './app.component';
import { ProductsComponent } from './products/products.component';
import { ProductComponent } from './product/product.component';

@NgModule({
  declarations: [
    AppComponent,
    ProductsComponent,
    ProductComponent
  ],
  imports: [
    BrowserModule
  ],
  providers: [ProductService],
  bootstrap: [AppComponent]
})
export class AppModule { }
import { ProductService } from './../service/product.service';
import { Component, OnInit } from '@angular/core';
import { Product } from '../models/product';

@Component({
  selector: 'app-products',
  templateUrl: './products.component.html',
  styleUrls: ['./products.component.css']
})
export class ProductsComponent implements OnInit {

  products: Product[] = [];

  constructor(public productService: ProductService) {
    this.products = productService.getProducts();
  }

  ngOnInit() {
  }

  deleteProduct(product: Product) {
    this.productService.removeProduct(product);

    this.products = this.productService.getProducts();
  }

}
  • We have seen the purpose of Service.
  • We have created ProductService, in that service we have created getProducts() and removeProduct() method.
  • After that, we have provided ProductService in AppModule.
  • We have injected the ProductService in ProductsComponent.
  • At the end, we have consumed the ProductService methods in ProductsComponent.