Services in Angular

Learn via video courses

Angular emphasizes modularity by differentiating between components, which manage user interfaces, and services, dedicated to specialized functionalities like data retrieval and validation. Components in Angular primarily focus on mediating between views or templates and underlying application logic. On the other hand, services, implemented as distinct classes, handle specific tasks such as server interactions and input validation. This separation ensures code clarity, maintainability, and scalability. Utilizing Angular's dependency injection mechanism, components seamlessly integrate with services, fostering efficient application development. This architectural approach not only enhances code organization but also facilitates adaptability, allowing developers to swap providers based on specific needs. Overall, Angular's structured approach to components and services empowers developers to craft robust, modular, and adaptable applications with enhanced reusability.

What is Dependency Injection?

Dependency injection is a design approach that allows a class to obtain dependencies from outside sources.

In other words, it is the practice of creating things in such a manner that instances of the objects may be obtained from other pieces of code.

The injection can be done through the constructor.

Dependency injection is a basic concept in Angular. It is built into the Angular framework and allows classes that use Angular decorators, such as Components, Directives, Pipes, and Injectables, to configure the dependencies they need.

The DI system has two fundamental roles: dependency consumer and dependency supplier.

Because Angular relies on services and dependency injection to offer access to built-in functionality, they are difficult to avoid. However, defining services for your own custom functionality is optional if that is your inclination.

Multiple service instances (sandboxing)

Multiple instances of service at the same level of the component hierarchy are sometimes required.

A service that keeps state for its associated component instance is a nice example. Each component requires its own instance of the service. Each service has its own work state that is distinct from the service and state of another component. This is referred to as sandboxing because each service and component instance has its own sandbox to play.

Qualify Dependency Lookup with Parameter Decorators

When a class requires a dependence, the dependency is sent as an argument to the constructor. When Angular wants to instantiate the class, it uses the DI framework to deliver the dependencies. By default, the DI framework searches for a provider in the injector hierarchy, beginning with the component's local injector and then bubbling up the injector tree until it reaches the root injector.

  • The dependency is supplied to the constructor by the first injector configured with a provider.
  • If no providers are identified in the root injector, the DI framework produces an error.

There are several ways to alter the default search behavior by using parameter decorators on the service-valued arguments of a class constructor.

Make a Dependency @Optional and Limit Search with @Host

Dependencies can be added at any level of the component hierarchy. When a component requests a dependency, Angular begins with the injector for that component and works its way up the injector tree until it finds the first acceptable provider. If Angular cannot discover the dependency during the walk, it throws an error.

The @Optional property decorator instructs Angular to return null if the dependency cannot be found.

The @Host property decorator instructs Angular to stop the upward search at the host component. Typically, the component seeking the dependency is the host component. When this component is projected into a parent component, the parent component takes on the role of the host.

Supply a Custom Provider with @Inject

You may offer a concrete implementation for implicit dependencies, such as built-in browser APIs, by using a custom provider. The InjectionToken is used in the following example to offer the localStorage browser API as a dependency in the BrowserStorageService.

The localStorage attribute of the browser window object is returned by the factory method. The Inject decorator is a constructor argument that is used to provide a custom dependency provider. Instead of communicating with real browser APIs, this custom provider may now be overridden during testing using a fake API of localStorage.

Modify the Provider Search with @Self and @SkipSelf

@Self: The @Self decorator informs Angular to search exclusively in the local injector for the dependency. The injector that is part of the present component or directive is known as the local injector.

@skipSelf: The @SkipSelf decorator encourages Angular to seek in the Parent Injector and above for the dependency.

It tells Angular not to look for the injector in the local injector, but start from the Parent. You can think of this decorator as the opposite of the @Self.

Inject the Component's DOM Element

DOM access is required by many visual effects and third-party programs, including jQuery, despite developers' best efforts to avoid it. As a result, you may need to access the DOM element of a component.

Here, angular assigns the inserted ElementRef to the constructor's el argument. (An ElementRef is a DOM element wrapper, and its nativeElement property exposes the DOM element for the directive to alter.)

Defining Providers

A provider is an object that you declare to Angular so that it may be injected in the constructor of your Angular-instantiated components, directives, and other classes.

The providers array shows how you might use the different provider-definition keys: useValue, useClass, useExisting, or useFactory.

Value Providers: useValue

When you wish to offer a basic value, use the Value Provider useValue.

Angular will inject whatever is specified in the useValue as it is. It is handy in situations where you need to offer an API URL, an application-wide configuration option, and so on.

  • Instead of forcing the injector to build a new instance with new or utilize its own cached instance, the first supplies an existing instance of the Hero class to use for the Hero token. The token in this case is the class itself.

  • The second provides a literal string resource for the TITLE token to be used. The TITLE provider token is not a class, but rather a type of provider lookup key known as an injection token, which is represented by an InjectionToken instance.

Class Providers: useClass

When you wish to supply an instance of the supplied class, use the Class Provider useClass.

The useClass method requires us to give a type. The Injector generates and injects a new instance of the type. It's the same as calling the new operator and returning the instance. If the type has any constructor parameters, the injector will additionally resolve those.

  • The first provider is a contra, enlarged version of the most common situation, in which the class to be produced (HeroService) also serves as the provider's dependency injection token. The short version is normally chosen, however, this extended form clarifies the specifics.

  • The second provider replaces LoggerService with DateLoggerService. At the AppComponent level, LoggerService is already registered. When this child component asks for LoggerService, it is instead given a DateLoggerService instance.

Alias Providers: useExisting

Make use of Aliased Provider useExisting, When you wish to utilize the new provider instead of the old one.

The useExisting provider key lets you map one token to another. In effect, the first token is an alias for the service associated with the second token, creating two ways to access the same service object.

Factory Providers: useFactory

The Factory Provider useFactory expects us to provide a function. It invokes the method and injects the result. We can also pass optional arguments to the factory function using the deps array. The deps array indicates how the arguments should be injected.

When we wish to return an object depending on a condition, we normally utilize the useFactory method.

The injector supplies the dependence value by running a factory function that you provide as the useFactory key value. There is a third key, deps, in this kind of provider that defines dependencies for the useFactory method.

Services in Angular

Services in Angular allow you to design code or capabilities that are subsequently available and reused in many other Angular project components. Services assist in the abstraction of logic and data that are hosted independently but may be used by other components.

These standards are not enforced by Angular. Angular, on the other hand, assist you in sticking to these standards by making it simple to component your application logic into services. Dependency injection in Angular makes such services available to components.

Service Examples

An example of a service class that logs to the browser console

A HeroService that depends on the Logger service, and also uses BackendService to get heroes

Providing Services

You must register at least one service provider for each service you want to utilize. The provider can be included in the service's metadata, making it available everywhere, or it can be registered with specific modules or components. You register providers in the service's metadata.

Injectable & Injectors

Injectable: The Injectable is a decorator that must be added to the dependency's consumer. This decorator instructs Angular that the constructor parameters must be injected via the Angular DI mechanism.

Injector: The dependency is instantiated and injected into the component or service by the Angular Injector. Using the Injection token, the Injector searches for the dependency in the Angular Providers. The Angular Providers array yields the Provider, which includes information on how to generate the dependent instance. The instance is created by the Injector and injected into the Component or service.

Providing Dependency

Assume there is a HeroService class that has to be used as a dependency in a component.

At Component

When you register a provider at the component level, you get a new instance of the service with each new instance of that component.

At ngModule

When you register a provider with a specific NgModule, the same instance of a service is available to all components in that NgModule.

providedIn: 'root'

Angular produces a single, shared instance of the HeroService and injects it into any class that requests it when you supply the service at the root level. By registering the provider in the @Injectable metadata, Angular may optimize an app by deleting the service from the generated application if it is not utilized, a technique known as tree-shaking.

Injecting a Dependency

Declaring a dependence in a class constructor is the most typical technique to inject it. When Angular generates a new instance of a component, directive, or pipe class, it looks at the constructor argument types to identify which services or other dependencies the class need.

Conclusion

  • Services that are hosted independently but may be accessed by other components, help in the abstraction of logic and data.
  • Dependency injection is used to define services in Angular.
  • @Injectable is used to define services.
  • There are four methods to define providers:
    • useValue
    • useClass
    • useExisting
    • useFactory