Self, SkipSelf And Optional Decorators In Angular
The Angular Decorators @Self, @SkipSelf, @Optional, and @Host control how the DI Framework resolves dependencies. Because they change the behavior of injectors, these decorators are called Resolution Modifiers. In this article, We will learn about the @Self, @SkipSelf, and @Optional.
How Angular DI Framework Resolves Dependencies
The DI Framework resolves dependencies in two phases when a component requests them.
It starts looking for the Dependency in the current component's ElementInjector in the first step. It will look in the Parent Components ElementInjector if it does not supply the Dependency. The Request bubbles up until it reaches the root ElementInjector or finds an injector that delivers the service.
If ElementInjector fails to satisfy the request, Angular searches the ModuleInjector hierarchy for the Dependency. If Angular is still unable to locate the provider, an exception is thrown.
@Self, @SkipSelf & @Optional Example
To demonstrate the @Self, @SkipSelf, and @Optional directives, we developed an Angular project.
The code includes a RandomService that, when initialized, creates a Random Number. The Angular Service is added to the AppModule's Providers array. This service can be injected anywhere in our application.
@Injectable({ providedIn: "root" }) export class RandomService {
@Self
@Component({ selector: "my-grandChild", template: ` <div class="box"> GrandChildComponent => {{ randomNo }} <div class="dirbox" testdirective="">fdf</div> </div> `, providers: [] }) export class GrandChildComponent { randomNo; constructor(@Self() private randomService: RandomService) { this.randomNo = randomService.RandomNo; } }
@Component({ selector: "my-grandChild", template: ` <div class="box"> GrandChildComponent => {{ randomNo }} <div class="dirbox" testdirective="">fdf</div> </div> `, providers: [RandomService] }) export class GrandChildComponent { randomNo; constructor(@Self() private randomService: RandomService) { this.randomNo = randomService.RandomNo; } }
@SkipSelf
import { Component, SkipSelf, Self, Optional, Host } from "@angular/core"; import { RandomService } from "./random-service"; @Component({ selector: "my-grandChild", template: ` <div class="box"> GrandChildComponent => {{ randomNo }} <div class="dirbox" testdirective="">fdf</div> </div> `, providers: [RandomService] }) export class GrandChildComponent { randomNo; constructor(@SkipSelf() private randomService: RandomService) { this.randomNo = randomService.RandomNo; } }
@Optional
import { Component, SkipSelf, Self, Optional, Host } from "@angular/core"; import { RandomService } from "./random-service"; @Component({ selector: "my-grandChild", template: ` <div class="box"> GrandChildComponent => {{ randomNo }} <div class="dirbox" testdirective="">fdf</div> </div> `, providers: [] }) export class GrandChildComponent { randomNo; constructor(@Optional() @Self() private randomService: RandomService) { this.randomNo = randomService?.RandomNo; } }
import { Component, VERSION } from "@angular/core"; import { RandomService } from "./random-service"; @Component({ selector: "my-app", providers: [], viewProviders: [], template: ` <div class="box"> <p>AppComponent => {{ randomNo }}</p> <my-child></my-child> </div> ` }) export class AppComponent { randomNo; constructor(private randomService: RandomService) { this.randomNo = randomService.RandomNo; } }
The following is complete code of the child.component.ts file
import { Component, SkipSelf, Self, Optional, Host } from "@angular/core"; import { RandomService } from "./random-service"; @Component({ selector: "my-child", providers: [], viewProviders: [], template: ` <div class="box"> <p>ChildComponent => {{ randomNo }}</p> <my-grandchild></my-grandchild> </div> ` }) export class ChildComponent { randomNo; constructor(private randomService: RandomService) { this.randomNo = randomService.RandomNo; } }
The following is complete code of the grand-child.component.ts file
import { Component, SkipSelf, Self, Optional, Host } from "@angular/core"; import { RandomService } from "./random-service"; @Component({ selector: "my-grandChild", template: ` <div class="box"> GrandChildComponent => {{ randomNo }} <div class="dirbox" testdirective="">fdf</div> </div> `, providers: [] }) export class GrandChildComponent { randomNo; constructor(private randomService: RandomService) { this.randomNo = randomService.RandomNo; } }
The following is complete code of the test-directive.ts file
import { Directive, ElementRef, Input, OnInit, SkipSelf, Self, Optional, Host } from "@angular/core"; import { RandomService } from "./random-service"; @Directive({ selector: "[testDirective]", providers: [] }) export class testDirective implements OnInit { @Input() ttClass: string; constructor(private el: ElementRef, private randomService: RandomService) {} ngOnInit() { this.el.nativeElement.innerHTML = "Directive =>" + this.randomService.RandomNo; } }
The following is complete code of the random-service.ts file
import { Injectable } from "@angular/core"; @Injectable({ providedIn: "root" }) export class RandomService { private _randomNo = 0; constructor() { console.log("RandomService Constructed"); this._randomNo = Math.floor(Math.random() * 1000 + 1); } get RandomNo() { return this._randomNo; } }
The following is complete code of the styles.css file
.box { margin: 5px; border: 1px; border-style: solid; } .dirbox { margin: 5px; border: 1px; border-style: dotted; }
The following is complete code of the app.module.ts file
import { NgModule } from "@angular/core"; import { BrowserModule } from "@angular/platform-browser"; import { FormsModule } from "@angular/forms"; import { AppComponent } from "./app.component"; import { ChildComponent } from "./child.component"; import { GrandChildComponent } from "./grand-child.component"; import { testDirective } from "./test-directive"; @NgModule({ imports: [BrowserModule, FormsModule], declarations: [ AppComponent, ChildComponent, GrandChildComponent, testDirective ], bootstrap: [AppComponent] }) export class AppModule {}