Angular Services Example using @Injectable()

By Arvind Rai, February 13, 2024
This page will walk through Angular services example using @Injectable() decorator. Angular services are injectable and injector can inject it in any component or service in our Angular application. The @Injectable() decorator is used in service at class level. The @Injectable() decorator helps the injector to consider the service eligible to inject. After configuring our service in providers metadata of @NgModule decorator in application module, the service becomes available globally.
To get instance of service in our component, we need to create a constructor with arguments of our service types. A service contains methods that can be used by components or other services.
Suppose, we want to create a shopping cart application. In this case, the service can contain methods to manage store items, methods to add item to cart, methods to remove item from cart, methods to search item etc.
Here in our example, we will create a simple shopping cart application. To fetch items, we will use HTTP and non HTTP mock data.
Now find the complete Angular services example step-by-step.

Steps to Create Angular Service

To create angular service we need to follow below steps.
Step-1: Create a class decorated with @Injectable()
@Injectable()
export class ItemService {
} 
@Injectable() decorator is a marker used at class level. It tells Injector that this class is available for creation by Injector. We use @Injectable() in our service class so that the service object can be created automatically for dependency injection in any component or any other service class. @Injectable() is also responsible to instantiate an angular component, pipe, directive etc. This becomes possible because @Component, @Pipe and @Directive decorators use @Injectable decorator. If our service class decorated with @Injectable() has dependency of other service classes, then we need not to worry for its creation and dependency injection will be performed by Injector automatically.

Step-2: Configure service name in providers metadata with @NgModule in module file.
@NgModule({
  ----
  ----
  providers: [
        ItemService
  ],
  ----
  ----
})
export class AppModule { } 
A component can use service only if we configure service in providers metadata of @NgModule. We can also configure service in providers metadata of @Component but in this case the service will be available to only this component. When we configure service in providers metadata of @NgModule decorator in module file then that service will be available globally in the application.

We can also use providedIn: 'root' attribute in @Injectable decorator to make available service globally.
@Injectable({
    providedIn: 'root'
})
export class ItemService {
} 


Step-3: Inject service in Component.
@Component({
   selector: 'store-app',
   templateUrl: './store.component.html' 
})
export class StoreComponent { 
   constructor(private itemService: ItemService) { }
} 
To get service available in our component we need to create a constructor with an argument of our service type in private scope. When component is loaded, service will be injected by the injector. Now we are ready to call service methods in our component. In the above code snippet, our service instance is itemService that will be used to call methods of ItemService service.

Angular Service Example with Shopping Cart

We will create an angular shopping cart application that will have one service and two components one for product store and another for cart. When the application will be loaded the store component will fetch items from the service and will display in its store front. When user will select item to add to cart, then item id will be passed to service from store component. Now service will add item into selected item that will be displayed by cart. When item needs to remove from cart then cart component will send item id to service and service will remove this item from selected items for cart.

1. Project Structure

Find the project structure.
angular-demo
|
|--src
|   |
|   |--app 
|   |   |
|   |   |--services
|   |   |   |
|   |   |   |--item.service.ts
|   |   |   |--item.ts
|   |   |   |--mock-items.ts   
|   |   |
|   |   |--store
|   |   |   |
|   |   |   |--store.component.ts
|   |	|   |--store.component.html
|   |	|   |--store.component.css
|   |   | 
|   |   |--cart    
|   |   |   |
|   |   |   |--cart.component.ts
|   |   |   |--cart.component.html
|   |   |   |--cart.component.css
|   |   |
|   |   |--app.component.ts
|   |   |--app.module.ts 
|   | 
|   |--main.ts
|   |--index.html
|   |--styles.css
|
|--node_modules
|--package.json 

2. Create Mock Data

To display product item in store front we are considering a JSON data. We will create an item class that will have item property related to product. We will create a constant that will store JSON data as an array of items. Find the item class.
services\item.ts
export interface Item {
   id: number;
   name: string;
   price: string;
   description: string;
} 
Find the constant file.
services\mock-items.ts
import { Item } from './item';

export const ITEMS: Item[] = [
  {"id": 1, "name": "Product 1", "price": "25.50", "description": "Product 1 description"},
  {"id": 2, "name": "Product 2", "price": "15.20", "description": "Product 2 description"},
  {"id": 3, "name": "Product 3", "price": "13.50", "description": "Product 3 description"},
  {"id": 4, "name": "Product 4", "price": "26.40", "description": "Product 4 description"},
  {"id": 5, "name": "Product 5", "price": "11.50", "description": "Product 5 description"},
  {"id": 6, "name": "Product 6", "price": "35.70", "description": "Product 6 description"}
]; 

3. Create Service

We will create service now. Create a class decorated with @Injectable().
services\item.service.ts
import { Injectable } from '@angular/core';
import { Item } from './item';
import { ITEMS } from './mock-items';

@Injectable()
export class ItemService {
	selectedItems: Item[] = [];
	getItems(): Item[] {
		return ITEMS;
	}
	getSelectedItems(): Item[] {
		return this.selectedItems;
	}
	addItem(id: number): void {
		const item = ITEMS.find(ob => ob.id === id) ?? {} as Item;
		if (this.selectedItems.indexOf(item) < 0) {
			this.selectedItems.push(item);
		}
	}
	removeItem(id: number): void {
		const item = this.selectedItems.find(ob => ob.id === id) ?? {} as Item;
		const itemIndex = this.selectedItems.indexOf(item);
		this.selectedItems.splice(itemIndex, 1);
	}
} 
In our service class we have created four methods.
getItems(): This is accessed by store component to display store items.
getSelectedItems(): This is accessed by cart component to display cart items.
addItem(): This is called by store component when "Add to Cart" button is clicked.
removeItem(): This is called by cart component when "Remove" button is clicked.

4. Create Components and HTML Templates

We will create two components and its HTML templates. One component will handle store front and another component will handle cart. Find the store component.
store\store.component.ts
import { Component, OnInit } from '@angular/core';
import { Item } from '../services/item';
import { ItemService } from '../services/item.service';

@Component({
  selector: 'store-app',
  templateUrl: './store.component.html', 
  styleUrls: ['./store.component.css']
})
export class StoreComponent implements OnInit { 
   storeItems: Item[] = [];
   constructor(private itemService: ItemService) { }
   getStoreItems(): void {
        this.storeItems = this.itemService.getItems();
   }
   ngOnInit(): void {
        this.getStoreItems();
   }
   addItemInCart(id:number): void {
	    this.itemService.addItem(id);
   }
} 
store\store.component.html
<div ngClass="leftPanel">
   <h3>Store</h3>
	<div ngClass="storeBox" *ngFor="let item of storeItems" >
	  <div>
		<div >
		  <h4><a href="#">{{item.name}}</a></h4>
		  <h4 >Price:{{item.price}}</h4>
		  <p>{{item.description}}</p>
		</div>
		<div>
		  <button type="button" (click)="addItemInCart(item.id)">Add to Cart</button>
		</div>
	  </div>
	</div>
</div> 
store\store.component.css
.leftPanel{float:left; width:40%}
.storeBox{float:left; width:170px;}
.rightPanel{float:left;}

h3{font-size:20px;} 
Find the cart component.
cart\cart.component.ts
import { Component, OnInit } from '@angular/core';
import { Item } from '../services/item';
import { ItemService } from '../services/item.service';

@Component({
  selector: 'cart-app',
  templateUrl: './cart.component.html', 
  styleUrls: ['./cart.component.css']
})
export class CartComponent implements OnInit { 
   cartItems: Item[] = [];
   constructor(private itemService: ItemService) { }
   getItemsForCart(): void {
        this.cartItems = this.itemService.getSelectedItems();
   }
   ngOnInit(): void {
        this.getItemsForCart();
   }
   removeItemFromCart(id:number): void {
        this.itemService.removeItem(id);
   }
} 
cart\cart.component.html
<div ngClass="rightPanel">
	<h3>Cart</h3>
	<ul *ngFor="let item of cartItems">
		<li>
			<span> {{item.name}} </span>
			<span><button type="button" (click)="removeItemFromCart(item.id)">Remove</button></span>
		</li>
	</ul>
</div> 
cart\cart.component.css
.leftPanel{float:left; width:40%}
.storeBox{float:left; width:170px;}
.rightPanel{float:left;}

h3{font-size:20px;} 


5. Create Application Component and Module

Find the angular module of our application. Here we will configure our service ItemService using providers metadata of @NgModule in our application module.
app.component.ts
import { Component } from '@angular/core';
@Component({
  selector: 'app-root',
  template: `              
			    <store-app> </store-app> 			   
			    <cart-app> </cart-app> 			  
           `
})
export class AppComponent { 
} 
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { HttpClientModule } from '@angular/common/http';
import { NgModule } from '@angular/core';
import { StoreComponent } from './store/store.component';
import { CartComponent } from './cart/cart.component';
import { AppComponent } from './app.component';
import { ItemService } from './services/item.service';

@NgModule({
      imports: [
            BrowserModule,
            HttpClientModule
      ],
      declarations: [
            AppComponent,
            StoreComponent,
            CartComponent
      ],
      providers: [
            ItemService
      ],
      bootstrap: [
            AppComponent
      ]
})
export class AppModule { } 

Angular Service with HTTP Request

Now we will create our shopping cart example using JSON data accessing from HTTP. In this example we will not use mock-items.ts file. Here we will access a URL using angular HTTP and populate data in store front. To enable HTTP, we need to add HttpModule in imports metadata of @NgModule in app.module.ts file. We will use HTTP in our service as follows.
observableItems: Observable<Item[]>;
constructor(private http: HttpClient) {
	this.observableItems = this.http.get<Item[]>(this.url);
	this.observableItems.subscribe(
		data => this.allItems = data,
		error => this.errorMessage = error);
}
getItems(): Observable<Item[]> {
	return this.observableItems;
} 
Find the description of HTTP API used in the above code snippet.
Observable: This is a stream of events that can be processed with array-like operators. Every HTTP service method returns an Observable.
http.get(): This access a HTTP URL using HTTP GET method and returns an Observable.
map(): This is a RxJS operator that extracts response object from response data.
subscribe(): It fetches events in the stream from Observable.

Now find the service and store component where we have done changes according to use HTTP method. Other files in our application will be same.
services\item.service.ts
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
import { Observable } from 'rxjs';
import { Item } from './item';

@Injectable()
export class ItemService {
	observableItems: Observable<Item[]>;
	allItems: Item[] = [];
	selectedItems: Item[] = [];
	errorMessage = '';
	url = "http://localhost:4200/assets/data/products.json";
	constructor(private http: HttpClient) {
		this.observableItems = this.http.get<Item[]>(this.url);
		this.observableItems.subscribe(
			data => this.allItems = data,
			error => this.errorMessage = error);
	}
	getItems(): Observable<Item[]> {
		return this.observableItems;
	}
	getSelectedItems(): Item[] {
		return this.selectedItems;
	}
	addItem(id: number): void {
		const item = this.allItems.find(ob => ob.id === id) ?? {} as Item;
		if (this.selectedItems.indexOf(item) < 0) {
			this.selectedItems.push(item);
		}
	}
	removeItem(id: number): void {
		const item = this.selectedItems.find(ob => ob.id === id) ?? {} as Item;
		const itemIndex = this.selectedItems.indexOf(item);
		this.selectedItems.splice(itemIndex, 1);
	}
} 
store\store.component.ts
import { Component, OnInit } from '@angular/core';
import { Item } from '../services/item';
import { ItemService } from '../services/item.service';

@Component({
  selector: 'store-app',
  templateUrl: './store.component.html',
  styleUrls: ['./store.component.css']
})
export class StoreComponent implements OnInit {
  storeItems: Item[] = [];
  errorMessage = '';
  constructor(private itemService: ItemService) { }
  getStoreItems(): void {
    this.itemService.getItems().subscribe(
      data => this.storeItems = data,
      error => this.errorMessage = error);
  }
  ngOnInit(): void {
    this.getStoreItems();
  }
  addItemInCart(id: number): void {
    this.itemService.addItem(id);
  }
} 
To create a sample HTTP URL using Angular CLI, create a file products.json and put it inside src/assets/data . Now we are ready with our HTTP URL as given below.
http://localhost:4200/assets/data/products.json 
This will return product JSON data for store front.

Output

Find the print-screen of the output.
http://localhost:4200/ 
Add some items in cart. Find the print screen.
Angular Services Example using @Injectable()

References

Angular: SERVICES
Angular: @Injectable

Download Source Code

POSTED BY
ARVIND RAI
ARVIND RAI







©2024 concretepage.com | Privacy Policy | Contact Us