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).
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.
- Note
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 createdgetProducts()
andremoveProduct()
method. - After that, we have provided
ProductService
inAppModule
. - We have injected the
ProductService
inProductsComponent
. - At the end, we have consumed the
ProductService
methods inProductsComponent
.