Wednesday, June 10, 2020

Angular - Call One Service From Another

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’ have to relay 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.

call one service from another

On clicking name, details are displayed.

Angular service dependency on another service

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 implements OnInit{
    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);
            }
        )
    }
    ngOnInit() {
        //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="jumbotron">
        <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

  1. Injector Hierarchy and Service Instances in Angular
  2. Angular Disable Button Example
  3. Angular ngStyle Directive With Examples
  4. Angular Two-Way Data Binding With Examples

You may also like-

  1. Angular Attribute Binding With Examples
  2. How to Install Node.js and NPM in Windows
  3. Angular Application Bootstrap Process
  4. CopyOnWriteArrayList in Java
  5. Literals in Java
  6. List in Python With Examples
  7. Spring Transaction Management JDBC Example Using @Transactional Annotation