Understanding Services and Dependency Injection in Angular

Introduction

In this Article, we will understand how to create a service and use dependency injection to inject that service into any component that needs it. Applications often must services such as a vehicle data service or a logging service. So, the components depend on services to do the heavy lifting.

What is service?

The service is a class with a focused purpose. We often create a service to implement functionality that is independent of any particular component to share data or logic. across components or encapsulate external interactions such as data access. to shifting these responsibilities from the component to a service, the code is easier to test, debug, and reuse. so, In this article, we start with an overview of how services and dependency injection work in Angular. then we’ll build a service and register that service.

How Does It Work?

Let’s go and understand how services and dependency injection work in Angular. In this example, our service is here

//Service
export class myService 
{
}

so, our component that needs the service is here.

//Component

let value = new myService();

There are two ways our component can work with this service. The component can create an instance of the service class and use it. That simple, and it works. But the instance is local to the component, so we can’t share data or other resources, and it will be more difficult to mock the service for testing.

Before that, we can register the service with Angular. Angular then creates a single instance of the service class, called a singleton, and holds onto it. Angular provides a built-in injector. We register our services with the Angular injector. which maintains a container of created service instances. The injector creates and manages the single instance, or singleton, of each registered service as required.

In this example,

//components

constructor(private _myservice)
{
}

The Angular injector is managing instances of different services. If our component needs a service, the component class defines the service as a dependency. The Angular injector then provides or injects. the service class instance when the component class instantiate. This process call dependency injection.

Since, Angular manages the single instance, any data or logic in that instance share by all the classes that use it. This technique is the recommended way to use services because it provides better management of service instances. it allows sharing of data and other resources, and it’s easier to mock the services for testing purposes.

What is Dependency injection?

The Dependency injection is a coding pattern in which a class receives the instances of objects it needs, called its dependencies, from an external source rather than creating them itself. In Angular, this external source is the Angular injector. Now that we’ve got a general idea of how services and dependency injection work in Angular, let’s build service.

Building a Service

Let’s Create the service class, define the metadata with a decorator, and import what we need. Here is the class. We export it so the service can be used from any other parts of the application.

//vehicle.service.ts

import { Injectable } from '@angular/core'

@Injectable()
export class VehicleService 
{
  getVehicle() : IVehicle[]
  {
	return [
	{
	 "VehicleID":1,
	 "VehicleName":"Mercedes"
	 "Price":45000$,
	 "color":"red"	
	},
	{
	 "VehicleID":2,
	 "VehicleName":"Tesla"
	 "Price":40000,
	 "color":"white"	
	}
	
	]
  }
}

This class currently has one method, getVehicle. This method returns an array of Vehicles. we add a decorator for the service metadata. When building services, we often use the Injectable decorator.

For our vehicle service, we want a getVehicles method that returns the list of vehicles. We strongly type this return value using our IVehicle interface. so we need to import this interface.

we have no properties defined in this class. so we are not using this particular service to share data. we are using it to encapsulate the data access features. By using this service to provide a list of
Vehicles. we take responsibility for managing the data away from the individual component. That makes it easier to modify or reuse this logic.

Registering the Service

As we illustrated in this diagram, we register the service with the Angular injector, and the injector provides the service instance to any component that injects it using the constructor.

The injector represented here is the root application injector. But wait, there’s more. In addition to the root application injector, Angular has an injector for each component, mirroring the component tree. A service registered with the root application injector is available to any component or other service in the application.

A service registered with a specific component is only available to that component and its child or nested components. For example, if a service register with the for injection in the vehicles list component and its child. the star component. So, when should you register your service with a root injector versus a component injector.

Registering a service with a root injector ensures that the service is available throughout the application. In most scenarios, you’ll register the service with the root injector. If you register a service with a component injector, the service is only available to that component and its child or nested components. This isolates a service that use by only one component and its children. and it provides many instances of the service for many instances of the component.

For example,

vehicle.service.ts
import { Injectable } from '@angular/core'

@Injectable({
  providedIn: 'root'
})
export class VehicleService {
 
  getVehicles(): IVehicle[]
  {
  }
}

Here in the service, we add the provided property to the Injectable decorator and set it to root. An instance of the vehicles service is then available for injection anywhere in the application.

//vehicle-list.component.ts

@Component({
	templateurl: './vehicle-list.component.html',
	providers: [VehicleService]
})
export class VehicleListComponent 
{
}

Injecting the Service

Now we need to define it as a dependency so the injector will provide the instance in the classes that need it. So, dependency injection in Angular using the constructor. Every class has a constructor that execute when an instance of the class is created. If there is no explicit constructor defined for the class, an implicit constructor is used. But if we want to inject dependencies such as an instance of a service, we need an explicit constructor.

//vehicle-list.component.ts

@Component({
	templateurl: './vehicle-list.component.html',
	providers: [VehicleService]
})
export class VehicleListComponent 
{
	private _vehicleService;
	
	constructor(vehicleService: VehicleService)
	{
	  this._vehicleService = vehicleService;
	}
}

In TypeScript, a constructor define with a constructor function. What type of code goes into the constructor function? As little as possible. Since the constructor function is executed when the component is created, it is primarily used for initialization and not for code that has side effects or takes time to execute. We identify our dependencies by specifying them as parameters to the constructor function.

So, the Angular injector sets this parameter to the injected instance of the requested service. and then assign the injected service instance to our local variable. We can then use this variable anywhere in our class to access service properties or methods.

Leave a Reply

Your email address will not be published. Required fields are marked *