Services & Dependency Injection
Services are used to share data and functionality between components. Services are also used to perform tasks that are not related to a specific component, such as logging, authentication, and data fetching. Services are singletons, which means that there is only one instance of a service in an application. Services are injected into components using dependency injection.
Creating a new service
To create a new service, run the following command in your terminal:
ng generate service my-service
where my-service is the name of your service.
You can also create a new service with the help of your IDE, for example, in WebStorm, you can go to File > New > Angular Schematic, type service and then enter the name of your service.
A sample service looks like this:
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root'
})
export class MyService {
}
To declare an Angular service, you need to create a class with the same name as the service. The class should be decorated with the @Injectable
decorator. The @Injectable
decorator contains metadata about the service. The metadata includes the providedIn property. The providedIn property specifies the root injector for the service. The root injector is the application-level injector that is responsible for creating services and injecting them into components.
Injecting a service into a component
To inject a service into a component, you need to add the service to the constructor of the component. For example, the following code injects the MyService service into the HomeComponent component:
import { Component } from '@angular/core';
import { MyService } from '../my-service.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: [ './home.component.scss' ]
})
export class HomeComponent {
constructor(private myService: MyService) {
}
}
Using a service for data sharing
Services may be used to share data between components. For example, the following code creates a service that contains a property called title. One component sets the value of the title property, and another component gets the value of the title property, with the help of listeners:
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';
@Injectable({
providedIn: 'root'
})
export class TitleService {
private titleSubject = new Subject<string>();
title$ = this.titleSubject.asObservable();
setTitle(title: string) {
this.titleSubject.next(title);
}
}
import { Component } from '@angular/core';
import { TitleService } from '../title.service';
@Component({
selector: 'app-home',
templateUrl: './home.component.html',
styleUrls: [ './home.component.scss' ]
})
export class HomeComponent {
constructor(private titleService: TitleService) {
}
handleClick() {
this.titleService.setTitle('Home');
}
}
import { Component } from '@angular/core';
import { TitleService } from '../title.service';
@Component({
selector: 'app-about',
templateUrl: './about.component.html',
styleUrls: [ './about.component.scss' ]
})
export class AboutComponent {
title = '';
constructor(private titleService: TitleService) {
this.titleService.title$.subscribe(title => {
this.title = title;
});
}
}
Services are also used for fetching data from a server
For example, the following code creates a service that fetches data from a server:
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class UserService {
constructor(private http: HttpClient) {
}
getUsers() {
return this.http.get('https://jsonplaceholder.typicode.com/users');
}
}
More about HttpClient
The Http methods return an Observable from rxjs library. Request method that we use determines the type of Observable returned. Here are the commonly used Http methods:
- get(): Used for requesting data from the given URL.
- post(): Used for sending data to the server.
- put(): Used for updating a resource on the server.
- delete(): Used for deleting a resource on the server.
import { Injectable } from '@angular/core';
import { HttpClient } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class UserService {
apiUrl = 'https://jsonplaceholder.typicode.com/users';
constructor(private http: HttpClient) {}
// Fetching data with get()
getUsers() {
return this.http.get(this.apiUrl);
}
// Sending data with post()
createUser(user: any) {
return this.http.post(this.apiUrl, user);
}
// Updating data with put()
updateUser(id: string, updateUser: any) {
return this.http.put(`${this.apiUrl}/${id}`, updateUser);
}
// Deleting data with delete()
deleteUser(id: string) {
return this.http.delete(`${this.apiUrl}/${id}`);
}
}
Remember to subscribe to the Observable returned by these methods to initiate the HTTP request.
this.userService.getUsers().subscribe((users: any) => {
console.log(users);
});
One good practice is also to handle error with the help of catchError.
import { catchError } from 'rxjs/operators';
getUsers() {
return this.http.get(this.apiUrl).pipe(catchError(this.handleError));
}
When making HTTP requests, it's quite common that we need to add query parameters to our URL. Query parameters are a defined set of parameters attached to the end of a url, and they give the server additional information about that request. In HttpClient, we can easily add query parameters to our HTTP requests by using the params option. Here is an example of using query parameters with the get() method in HttpClient:
import { Injectable } from '@angular/core';
import { HttpClient, HttpParams } from '@angular/common/http';
@Injectable({
providedIn: 'root'
})
export class UserService {
apiUrl = 'https://jsonplaceholder.typicode.com/users';
constructor(private http: HttpClient) {}
// Fetching data with get() and query params
getUsersByCompany(companyName: string) {
let params = new HttpParams().set('company', companyName);
return this.http.get(this.apiUrl, { params });
}
}