In the Service in Angular With Examples post we saw how Components in Angular should only concentrate on view-related functionality. Any application logic like fetching data through server calls, user input validation, logging to console should be delegated to a service in Angular. It doesn’t mean that all the application logic for a component has to go in a single service, you can create separate services for different functionalities and then call one service from another in Angular application. In this post we’ll see how to call one service from another in Angular.
Angular Service that needs another service
A service in Angular may have a dependency on another service, for example you may have a DataService
to get data from backend, SecurityService
to secure your app, LoggerService
to log to log file and these classes may have interdependency.
You don’t have to do much to configure dependency between services. A service that needs another service should have a @Injectable decorator and then you can apply the same constructor injection pattern where you provide the service dependency with in the constructor.
For example if there is a UserService that needs LoggerService for logging purpose can define the dependency in its constructor as shown below-
@Injectable({ providedIn: 'root', }) export class UserService { constructor(private loggerService: LoggerService){} .. .. }
Call one service from another Angular example
Let’s create a full example to see how to configure dependency for another service. We’ll revisit the example Angular @Input and @Output Example that way we can also show how using service classes reduce the complexity in your application by making the communication among several components easy. You don’t have to rely that much on providing custom property binding through @Input and providing custom event binding through @Output. Service class can act like a centralized class which facilitate communication among Angular components.
In the example user names are displayed and clicking any of these names displays the user details.
Initially user names are displayed.
On clicking name, details are displayed.
user.model.ts class
First thing is to define the User class with fields Name, Age, JoinDate.
export class User { name : string; age : number; joinDate : Date; constructor(name: string, age : number, joinDate : Date) { this.name = name; this.age = age; this.joinDate = joinDate; } }
user.component.html
Template for displaying user names.
<div class="row"> <div class="col-xs-12 col-md-8 px-3"> <p style="cursor:pointer" class="list-group-item" (click)="onClickUser()"> {{user.name}} </p> </div> </div>
UserComponent class
import { Component, OnInit, Input } from '@angular/core'; import { User } from './user.model'; import { UserService } from '../services/user.service'; @Component({ selector: 'app-user', templateUrl: './user.component.html', styleUrls: ['./user.component.css'] }) export class UserComponent implements OnInit { @Input() user: User; @Input() index: Number; constructor(private userService: UserService){} ngOnInit(): void { } onClickUser(){ this.userService.indexClicked.emit(this.index); } }
As you can see in UserComponent there is an input property binding for user which means it will get user data from another component. In the onClickUser() method it emits the index of the clicked user.
AppComponent class
That’s where user data is fetched from the service and passed on to the UserComponent to display user names.
import { Component, OnInit } from '@angular/core'; import { User } from './user/user.model'; import { UserService } from './services/user.service'; @Component({ selector: 'app-root', templateUrl: './app.component.html', styleUrls: ['./app.component.css'], }) export class AppComponent implements OnInit{ users: User[] = []; constructor(private userService: UserService){} ngOnInit(){ // get all the users this.users = this.userService.users; } }
app.component.html
<div class="container"> <div class="row"> <div class="col-md-5"> <div class="list-group"> <app-user *ngFor="let u of users; let i = index" [user]="u" [index]="i"></app-user> </div> </div> <div class="col-md-7"> <appuser-detail></appuser-detail> </div> </div> </div>
As you can see in *ngFor directive user array iteration is done and each user is assigned to the [user] property and index is also assigned to the [index] property binding.
UserService class
import { User } from '../user/user.model'; import { EventEmitter, Injectable } from '@angular/core'; import { LoggerService } from './logger.service'; @Injectable({ providedIn: 'root', }) export class UserService { // dependency on Logger Service constructor(private loggerService: LoggerService){} // User detail array users = [new User('Jack', 62, new Date('2005-03-25')), new User('Olivia', 26, new Date('2014-05-09')), new User('Manish', 34, new Date('2010-10-21'))] ; indexClicked = new EventEmitter<Number>(); getUserDetails(id: Number){ this.loggerService.log("getting user data for ID- " + id); return this.users[id.valueOf()]; } }
This is the class that has users stored in the array. There is a property indexClicked of type EventEmitter that emits a number. In the UserComponent class index of the selected user is emitted using this property. Method getUserDetails() returns user instance from the array for the passed index.
UserService has a dependency on LoggerService which is specified in the constructor. In the getUserDetails() method loggerService is used to log a message.
LoggerService class
Though it is better to use @Injectable decorator with provider for LoggerService too, just to show another way of providing service, provider for this service is registered in AppModule.
export class LoggerService { constructor() { } log(msg: string) { console.log(msg); } error(msg: string) { console.error(msg); } }
AppModule class
@NgModule({ declarations: [ AppComponent, UserComponent, UserDetailComponent ], imports: [ BrowserModule, AppRoutingModule ], providers: [LoggerService], bootstrap: [AppComponent] }) export class AppModule { }
UserDetailComponent class
This component has the property bindings for showing user details for the selected user.
import { Component, OnInit } from '@angular/core'; import { UserService } from '../services/user.service'; import { User } from './user.model'; @Component({ selector: 'appuser-detail', templateUrl: './userdetail.component.html' }) export class UserDetailComponent { id: Number; user: User; constructor(private userService: UserService){ this.userService.indexClicked.subscribe( (index: Number) => { this.id = index; console.log(this.id); this.user = this.userService.getUserDetails(this.id); } ) } }
In the UserService there is a property indexClicked that emits event. In this component we subscribe to that property and get the index. Using that index userService.getUserDetails() method is called by passing the index as argument.
userdetail.component.html
<div *ngIf="user"> <div class="mt-4 p-5 bg-light"> <h2>User Details</h2> <div class="row"> <div class="col-xs-5 px-3"> <label>Name: </label> {{ user.name }} </div> <div class="col-xs-4 px-3"> <label>Age: </label> {{ user.age }} </div> <div class="col-xs-4 px-3"> <label>Joining Date: </label> {{ user.joinDate | date:'dd/MM/yyyy'}} </div> </div> </div> </div>
Here *ngIf directive is used to show the details only when there is user data to be displayed.
That's all for this topic Angular - Call One Service From Another. If you have any doubt or any suggestions to make please drop a comment. Thanks!
>>>Return to Angular Tutorial Page
Related Topics
You may also like-
No comments:
Post a Comment