Welcome back to the Learn Angular 8 in 10 Days article series - Part 8. In the previous article, we discussed the different concepts of Form Control in Angular. Now, in this article, we will discuss the Concept of Angular Service. If you want to read the previous articles of this series, then follow the links.
So, in this article, we will discuss the concept of Service in Angular 8. Angular service plays an important role to communicate with the backend layer of any application from the component level to send or retrieve data. That’s why Service is another backbone of any angular application. So, it is very important to understand why it is required and how to use it in any applications?
In Angular Framework, services are always singleton objects which normally get instantiated only once during the lifetime of any application or module. Every Angular Service contains several methods that always maintains the data throughout the life of an application. It is a mechanism to share responsibilities within one or multiple components. As per the Angular Framework, we can develop any application using a nested relationship-based component. Once our components are nested, we need to manipulate some data within the different components. In this case, a service is the best way to handle this. Service is the best place where we can take data from other sources or write down some calculations. Similarly, services can be shared between multiple components as needed.Angular has greatly simplified the concept of services since Angular 1.x. In Angular 1x, there were services, factories, providers, delegates, values, etc., and it was not always clear when to use which one. So, for that reason, Angular 8 simply changed the concept of Angular. There are simply two steps for creating services in Angular:
In Angular, a service is used when a common functionality or business logic needs to be provided, written, or needs to be shared in a different name. Actually, a service is a totally reusable object. Assuming that our Angular application contains some of the components performing the logging for error tracking purposes, you will end up with an error log method in each of these components. As per the standard practice, it is a bad approach since we used an error log method multiple times in the application. If you want to change the semantics of error logging, then you will need to change the code in all these components, which will impact the whole application. So use a common service component for the error log features is always good. In this way, we can remove the error log method from all components and placed that code within a service class. Then components can use the instance of that service class to invoke the method. In Angular Framework, Service injection is one way of performing dependency injection.
Angular services are single objects that normally get instantiated only once during the lifetime of the Angular application. This Angular service maintains data throughout the life of an application. It means data does not get replaced or refreshed and is available all the time. The main objective of the Angular service is to use shared business logic, models, or data and functions with multiple different components of an Angular application.The main objective of using an Angular service is the Separation of Concern. An Angular service is basically a stateless object, and we can define some useful functions within an Angular service. These functions can be invoked from any component of the application elements like Components, Directives, etc. This will help us to divide the entire application into multiple small, different, logical units so that those units can be reusable.
We can create a user-defined custom service as per our requirement. To create a service, we need to follow the below steps:
NOTE
When we create our custom service available to the whole application through the use of a provider’s metadata, this provider’s metadata must be defined in the app.module.ts(main application module file) file. If you provide the service in the main module file, then it is visible to the whole application. If you provide it in any component, then only that component can use the service. By providing the service at the module level, Angular creates only one instance of the CustomService class, which can be used by all the components in an application.
import { Injectable } from '@angular/core';
@Injectable()
export class AlertService
{
constructor()
{ }
publish showAlert(message: string)
{
alert(message);
}
}
@Injectable is actually a decorator in Angular Framework. Decorators are a proposed extension in JavaScript. In short, a decorator provides the ability for programmers to modify or use methods, classes, properties, and parameters. In angular, every Injectable class actually behaves just like a normal class. That’s why the Injectable class does not have any special lifecycle in the Angular framework. So, when we create an object of an Injectable class, the constructor of that class simply executed just like ngOnInit() of the component class. But, in the case of Injectable class, there is no chance to define destructor because, in JavaScript, there is no concept of the destructor. So, in simple work, the Injectable service class can’t be the destroyer. If we want to remove the instance of the service class, then we need to remove the reference point of dependency injection related to that class.@Injectable decorator indicates Angular Framework that this particular class can be used with the dependency injector. @Injectable decorator is not strictly required if the class has other Angular decorators like a Component decorator, directive decorator, etc. on it or does not have any dependencies.
Most important is that any class needs to define with @Injectable() decorator so that it can be injected into the application.
@Injectable()
export class SampleService
{
constructor()
{
console.log('Sample service is created');
}
}
Similar to the .NET MVC Framework, Angular 8 also provides support for the Dependency Injection or DI. We can use the component constructor to inject the instances of the service class. Angular 8 provides the provider metadata in both Module level and Component level which can be used to perform Automatic Dependency Injection of any Injectable Service at the runtime.
Dependency Injection always is one of the main benefits of Angular Framework. Due to these benefits, Angular Framework is receiving much more appreciation and acceptation among developers which can not achieve by other related client-side frameworks. With the help of this feature, we can inject any types of dependency like service, external utility module in our application module. For doing this, we do not even want to know how those dependency modules or services have been developed.So, Angular Framework has its own mechanism for the Dependency Injection system. In this system, every angular module has its related own injector metadata values. According to that, the injector of each module is responsible for creating the dependent object reference point and then it will inject in the module we required. Actually, every dependency behaves like key-pair values where token act as a key and the instance of the object which needs to be injected act as a value. But in spite of this cool mechanism, there are some problems in the existing Dependency mechanism as below -
In Angular Framework, Dependency Injection contains three sections like Injector, Provider & Dependency.
So as per the above discussion, we can perform the below tasks using the Dependency Injection in Angular Framework,
So now, one question arises after the above discussion: what are these providers that injectors register at each level? A provider is an object or class that Angular uses to provide something we want to use:
Sample of a Class
export class testClass {
public message: string = "Hello from Service Class";
public count: number;
constructor() {
this.count=1;
}
}
Okay, that’s the class. Now, we need to use the Injectable() decorator so that Angular Framework can register that class a provider and we can use the instance of that class within the application. We’ll create a component that will serve as the root component of our application. Adding the testClass provider to this component is straightforward:
Add an argument of type “testClass” to the constructor
import { Component } from "@angular/core";
import { testClass} from "./service/testClass";
@Component({
module : module.id,
selector : ‘test-prog’,
template : ‘<h1>Hello {{_message}}</h1>’,
providers : [testClass]
})
export class TestComponent
{
private _message:string="";
constructor(private _testClass : testClass)
{
this._message = this._testClass.message;
}
}
Under the covers, when Angular instantiates the component, the DI system creates an injector for the component that registers the testClass provider. Angular then sees the testClass type specified in the constructor’s argument list, looks up the newly registered testClass provider, and uses it to generate an instance that it assigns to “_testClass”. The process of looking up the testClass provider and generating an instance to assign to “_testClass” is all Angular.
We can inject the Angular Service in Application Level or Module Level. The provider’s property of NgModule decorator gives us the ability to inject a list number of Angular services into the module level. It will provide a single instance of the Angular Service across the entire application and can share the same value within different components.
@NgModule({
imports: [ BrowserModule, FormsModule ],
declarations: [ AppComponent, ParentComponent, ChildComponent ],
bootstrap: [ AppComponent ],
providers: [ SimpleService, EmailService ] ①
})
class AppModule { }
We can also inject the Angular Service into the Component Level. Similarly like NgModule, providers’ property of Component decorator gives us the ability to inject a list number of Angular services within that particular component. It will provide a single instance of the Angular Service across the entire component along with the child component.
@Component({
selector: 'parent',
template: `...`,
providers: [ EmailService ]
})
class ParentComponent {
constructor(private service: EmailService) { }
}
Now in this demo, we will demonstrate how to use Injectable Service in Angular. For this purpose, we will develop an entry form related to the Student Information and on submit button click, we will pass those data into service so that it will store that data.
app.component.ts
import { Component, OnInit } from '@angular/core';
import { StudentService } from './app.service';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls : ['./custom.css']
})
export class AppComponent implements OnInit {
private _model: any = {};
private _source: Array<any>;
constructor(private _service: StudentService) {
this._source = this._service.returnStudentData();
}
ngOnInit(): void {
}
private submit(): void {
if (this.validate()) {
this._service.addStudentData(this._model);
this.reset();
}
}
private reset(): void {
this._model = {};
}
private validate(): boolean {
let status: boolean = true;
if (typeof (this._model.name) === "undefined") {
alert('Name is Blank');
status = false;
return;
}
else if (typeof (this._model.age) === "undefined") {
alert('Age is Blank');
status = false;
return;
}
else if (typeof (this._model.city) === "undefined") {
alert('City is Blank');
status = false;
return;
}
else if (typeof (this._model.dob) === "undefined") {
alert('dob is Blank');
status = false;
return;
}
return status;
}
}
app.component.html
<div style="padding-left: 20px">
<h2>Student Form</h2>
<table style="width:80%;">
<tr>
<td>Student Name</td>
<td><input type="text" [(ngModel)]="_model.name" /></td>
</tr>
<tr>
<td>Age</td>
<td><input type="number" [(ngModel)]="_model.age" /></td>
</tr>
<tr>
<td>City</td>
<td><input type="text" [(ngModel)]="_model.city" /></td>
</tr>
<tr>
<td>Student DOB</td>
<td><input type="date" [(ngModel)]="_model.dob" /></td>
</tr>
<tr>
<td></td>
<td>
<input type="button" value="Submit" (click)="submit()" />
<input type="button" value="Reset" (click)="reset()" />
</td>
</tr>
</table>
<h3>Student Details</h3>
<div class="ibox-content">
<div class="ibox-table">
<div class="table-responsive">
<table class="responsive-table table-striped table-bordered table-hover">
<thead>
<tr>
<th style="width:40%;">
<span>Student's Name</span>
</th>
<th style="width:15%;">
<span>Age</span>
</th>
<th style="width:25%;">
<span>City</span>
</th>
<th style="width:20%;">
<span>Date of Birth</span>
</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of _source; let i=index">
<td><span>{{item.name}}</span></td>
<td><span>{{item.age}}</span></td>
<td><span>{{item.city}}</span></td>
<td><span>{{item.dob}}</span></td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
</div>
app.service.ts
import { Injectable } from "@angular/core";
@Injectable()
export class StudentService {
private _studentList: Array<any> = [];
constructor() {
this._studentList = [{name:'Amit Roy', age:20, city:'Kolkata', dob:'01-01-1997'}];
}
returnStudentData(): Array<any> {
return this._studentList;
}
addStudentData(item: any): void {
this._studentList.push(item);
}
}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { StudentService } from './app.service';
@NgModule({
declarations: [
AppComponent
],
imports: [
BrowserModule, FormsModule, ReactiveFormsModule
],
providers: [StudentService],
bootstrap: [AppComponent],
schemas: [NO_ERRORS_SCHEMA]
})
export class AppModule { }
Now check the browser for the output,
Now, in this demo, we will discuss the module level injection of any Angular service. For that purpose, we will create two-component as a parent child-related i.e. Parent Component and Child Component. And then use the selector of the parent component in our Root component. For showing the module level service instance, we use two instances of Parent component within the Root component. The code samples as below,
parent.component.ts
import { Component, OnInit } from '@angular/core';
import { DemoService } from './app.service';
@Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls : ['./custom.css']
})
export class ParentComponent implements OnInit {
constructor(private demoService:DemoService){
}
ngOnInit(){
}
}
parent.component.html
<div class="parent">
<p>Parent Component</p>
<div class="form-group">
<input type="text" class="form-control" name="value" [(ngModel)]="demoService.message">
</div>
<child></child>
</div>
child.component.ts
import { Component, OnInit } from '@angular/core';
import { DemoService } from './app.service';
@Component({
selector: 'child',
templateUrl: './child.component.html',
styleUrls : ['./custom.css']
})
export class ChildComponent implements OnInit {
constructor(private demoService:DemoService){
}
ngOnInit(){
}
}
child.component.html
<div class="child">
<p>Child Component</p>
{{ demoService.message }}
</div>
app.component.ts
import { Component, OnInit } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls : ['./custom.css']
})
export class AppComponent implements OnInit {
ngOnInit(){
}
}
app.component.html
<div style="padding-left: 20px;padding-top: 20px; width: 500px;">
<div class="row">
<div class="col-xs-6">
<h2>Parent Componet - 1</h2>
<parent></parent>
</div>
<div class="col-xs-6">
<h2>Parent Componet - 2</h2>
<parent></parent>
</div>
</div>
</div>
custom.css
.parent{
background-color: bisque;
font-family: Arial, Helvetica, sans-serif;
font-weight: bolder;
font-size: large;
}
.child{
background-color:coral;
font-family: Georgia, 'Times New Roman', Times, serif;
font-weight: bold;
font-size: medium;
}
app.module.ts
import { BrowserModule } from '@angular/platform-browser';
import { NgModule, NO_ERRORS_SCHEMA } from '@angular/core';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';
import { AppComponent } from './app.component';
import { ParentComponent } from './parent.component';
import { ChildComponent } from './child.component';
import { DemoService } from './app.service';
@NgModule({
declarations: [
AppComponent,ParentComponent,ChildComponent
],
imports: [
BrowserModule, FormsModule, ReactiveFormsModule
],
providers: [DemoService],
bootstrap: [AppComponent],
schemas: [NO_ERRORS_SCHEMA]
})
export class AppModule { }
Now check the output into the browser,
In the above example, we see that if we input some text in any one parent component input box, it will automatically update the related nested child component along with another parent component also. This is occurred because of the DemoService is injected into the Module level. So it creates a single-tone instance of that service and that instance is available across the entire application.
In the previous sample, we see how to inject any Angular service into Module level and how if share the same data entered into one component to all components across the module. Now, in this demo, we will demonstrate what will happen if we inject any service into the Component level. For that purpose, we just make the below changes in the parent.component.ts file,
import { Component, OnInit } from '@angular/core';
import { DemoService } from './app.service';
@Component({
selector: 'parent',
templateUrl: './parent.component.html',
styleUrls : ['./custom.css'],
providers:[DemoService]
})
export class ParentComponent implements OnInit {
constructor(private demoService:DemoService){
}
ngOnInit(){
}
}
Now, check the browser for the output,
In the above example, we clearly see that when we type any input in the parent 1 component, it will not automatically be shared by another instance of the parent component. Due is occurred due to the Component level injection of the DemoService.
In this article, we discussed another important feature of Angular Framework i.e. Angular Service. Also, we discussed the basic concept of Angular along with its benefit and how to use it in an Angular Application. Now, in the next article, we will how to handle the ajax request in an Angular Application. I hope, this article will help you. Any feedback or query related to this article is most welcome.
Next Article …
#angular #angular8 #learn angular 8 #javascript #tutorial