Angular CheatSheet

 

Angular CheatSheet

Angualr Forms

Template- Driven forms

Key Features - Use FormModule and ngModel directive - Two-way data binding with [(ngModel)] - Validation attribute in html (required, minlength) - Form state traking : touchedvalidinvaliddirty

Define in template

<form #contactForm="ngForm" (ngSubmit)="OnSubmit(contactForm)">
    <input name="name" [(ngModel)]="name" required>
    <!-- other field and error we can defiene -->
     <button type="submit">Submit</button>
</form>

Usage

- Simple forms with basic validation, Quick response, Forms that mirror data directly, minimal component logic needed

Reactive Forms

Key Features

  • Uses ReactiveFormsModuleFormGroupFormControl
  • Programmatic control and validation
  • Custom validation: noSpaceValidator and abusiveWorldValidator
  • Better testability and scalability

Example

Template

<form [formGroup]="login" (ngSubmit)="onSubmit()">
    <input id="username" formControlName="username">
    @if(username.invalid && username.touched){
        @if(username.errors?.['required']){
            Username is required
        }
        @if(username.error?.['usernameTaken']){
            Username already taken
        }
        @if(username.hasError('noSpace')){
            Name cannot contain space!
        }
    }
    <!-- other fields  -->

</form>

Use:

  • Complex forms with dynamic validation
  • cross-field validation
  • Better control over from state
  • Easier unit testing

Form Arrays

FormArray is used to manage a dynamic collection of form controls.

Key Features

  • Dynamic form controls (add/ remove skills)
  • Uses FormArray for collection
  • Allow user to add/remove filed dynamically Implementation stapes
  1. in ts file
import { Component, OnInit } from '@angular/core';
import { FormGroup, FormBuilder, FromControl FormArray, Validators } from '@angular/forms';

@Component({
  selector: 'app-profile-editor',
  templateUrl: './profile-editor.component.html',
  styleUrls: ['./profile-editor.component.css']
})
export class ProfileEditorComponent implements OnInit {
    availableSkills = ['C#', 'Jave', 'Angular'];
    empFrom = new FormGroup({
        name: new FormControl('')
        skills: new FormArray([new FormControl('')])
    });
    //  Using FormBuilder
    //   empForm = this.fb.group({
    //     name: ['', Validators.required],
    //     skills: this.fb.array([
    //         this.fb.control('')
    //     ])
    //   });

    get skills(){
        return this.empForm.get('skills') as FormArray;
        // return (<FormArray>this.empForm.get('skills')) as FormArray
    }

    addSkill(){
        this.skills.push(new FormCntrol(''));
        // this.skills.push(this.fb.control(''));
    }
    removeSkill(index: number){
        this.skills.RemoveAt(index);
    }
    onSubmit(){
        console.log(this.empForm.value);
    } 
}
  1. Bind FormArray to template
<form [formGroup]="empForm" (ngSubmit)="onSubmit()">
    <input name="name" formControlName="name">
    <!-- other form control -->
     <div formArrayName="skills">
        <button type="button" (click)="AddSkill()">Add Skill</button>
        @for(skill of skills.controls; track $index){
           <input list ="skillsList" [formControlName]="$index">
           <button (click)="removeSkill($index)">Remove Skill</button>
        }
        <!-- data list for skills -->
        <datalist id="skillsList">
            @for(skill of availableSkills; track skill){
                <option [value]="skill">{{skill}}</option>
            }
        </datalist>
     </div>
</form>

Custom Validators

a custom validator is a function that you define to implement specific validation logic not covered by the built-in validators.

note: we can use custom validators in both tmplate-driven and reactive form as well.

Custom validators for reactive forms

Implementation stapes:

  1. Create the Validator Function
import { AbstractControl, ValidationErrors, ValidatorFn } from '@angular/forms';

export function abusivewordValidator(control : AbstractControl): ValicationError | null {
    const abusivewords = ['idot', 'dumb','fool'];
    const controlValue = (control.value as string).toLowerCase();
    const containAbusivewords = abusivewords.some(word => controlValue.include(word)
    );
    return containAbusivewords ? { abusiveword: true }: null;
}
  1. Add to a Form Control
    empFrom = new FormGroup({
        name: new FormControl('', Validators.required,abusivewordValidator )
        skills: new FormArray([new FormControl('')])
    });
    //  Using FormBuilder
    //   empForm = this.fb.group({
    //     name:['', [Validators.required, abusivewordValidator]],
    //     skills: this.fb.array([
    //         this.fb.control('')
    //     ])
    //   });

Custom validators for Template driven forms

  1. Create the Validator Directive
ng generate directive shared/abusive-word-validator
  1. Write Validator function in directive
import { Directive, Input } from '@angular/core';
import { AbstractControl, NG_VALIDATORS, ValidationErrors, Validator, ValidatorFn, Validators } from '@angular/forms';

@Directive({
  selector: '[appAbusiveWordValidator]',
  providers: [{ provide: NG_VALIDATORS, useExisting: AbusiveWordValidatorDirective, multi: true }]
})
export class AbusiveWordValidatorDirective implements Validator {
  // Input property to receive the list of abusive words from the template
    const abusivewords = ['idot', 'dumb','fool'];
    const controlValue = (control.value as string).toLowerCase();
    const containAbusivewords = abusivewords.some(word => controlValue.include(word)
    );
    return containAbusivewords ? { abusiveword: true }: null;
  
}

  1. Add the Directive to the Template
<input name="name" [(ngModel)]="name" appAbusiveWordValidator required>

Async Validators

Async validators in Angular are used for validation that requires an asynchronous operation. Async Validators return Promise or Observable and used for server-side validation:

Key Charaterstics:

  • Return Observable<ValidationErrors | null> or Promise<ValidationErrors | null>
  • Execute AFTER all sync validators pass
  • Provide a pending state during validation
  • Run on value changes with debouncing support

**NOTE: ** - Promises: Single value, not cancellable, simplar for one-time checks - Observable: Multipale value, cancellable(important for preventing race conditions), better for reactive programing with RxJS operators (recommendation).

// Custom async Validators
function userNameAvailable(userService: UserService): AsyncValidatorFn{
    return (control: AbstractControl): Observable<ValidationError | null > => {
        return userService.checkUserName(control.value).pipe(
            map(available => available ? null : {usernameTaken: true}),
            catchError(() => of(null))
        );
    };
}

//Usage
this.form = new FormGroup({
    username: new FormControl({
       '', 
       [Validators.required], // sync validators
       [usernameAvailableValidator(this.userService)] // Async Validators 
    })
});

When to use:

  • Check if username/email already exists
  • Validate data against database
  • External API validation (Credit Card verification, Address Verification)
  • Any validation required server call (like: Promo code verification, Phonenumber varification).

When NOT to Use:

  • Simple formate validation
  • Regex pattern (use Validators.pattern())
  • Min/Max length check (use sync validators)
  • Required filed validation

Important: Async validators run AFTER sync validators pass.

Note:

  • Performance: By default, async validators run on every value change. To avoid excessive API calls (e.g., on every keystroke), you can set the updateOn property to 'blur' or 'submit', or use RxJS operators like debounceTime(ms)distinctUntillChanged(), switchMap()` check empty value before making API call. UndateOn: 'blur' or 'submit': Validate only when user leaves the field or submit form.
    username: new FormControl('', {
        validators: [Validaotrs.required, Valifators.minLength(3)],
        asyncValidators: [usernameAsyncValidators()],
        updateOn: 'blur' // Only validate when user leaves the field or 
    // updateOn: 'submit'  // Only validate on form submission
    })
    
  • pending State: While an async validation is in progress, the form control enters a PENDING status. This state can be used in the template to show a loading indicator (e.g., a spinner).
  • Signature: An async validator function (implementing the AsyncValidatorFn interface) takes an AbstractControl instance as a parameter and returns an Observable<ValidationErrors | null> or Promise<ValidationErrors | null>.

Angular Directives

Directive: are maker on a DOM element (such as an attribute, element name, comment, or CSS class) that tell Angular's HTML compiler to attach a spescified behaviour to that element or even transform the DOM element and its children.

Key Charaterstics:

  • Extend HTML with new functionally
  • Add or modify DOM element behavior
  • Can add style, event listners, and attributes
  • Reusable across components
  • Three types Component, Structural, and Attribute

Classification:

   Directives
   |- Components (special directives with templates)
   |- Structural Directive (modify DOM structure)
       |- *ngIf
       |- *ngFor
       |- *ngSwitch
       |-Custom structural directives
   |- Attribute Directive (change element behaviour/appearance)
       |- Built-in: ngClass, ngStyle, ngModel
       |- Custom attribute directive

Type of Directives

Component Directive

A component directive is spacial kind with a trmplate, styling, a component logics.

   @Component({
       selector: 'app-user',
       template: `<div>{{name}}</div>`,
       styles: ['div {color: blue}']
   })
   export class UserComponent {
       @Input() name: String;
   }

Structural Directives

Modify the DOM structure by adding or removing elements.

<div *ngIf="isVisable">Visible</div>
<!-- ng-template use -->
 <div *ngIf="isVisible; thenblock else elseBlock"></div>
 <ng-template #thenBolck>Then Block</ng-template>
  <ng-template #elseBolck>Else Block</ng-template>
<!-- ngfor -->
 <!-- basic -->
<div *ngFor="let item of items">{{item}}</div>
<!-- with index -->
 <div *ngfor =" let item of items; let i=index">
<!-- with first, last, odd, even, count -->
 <div *ngFor="let item of items; let first=fisrt; let last = last; let odd = odd; let even = even; let count = count ">
<!-- Track by performance -->
 <div *ngfor =" let item of items; trackBy: tracByFn">
<!-- for trackBy implement in component 
    // Without trackBy : Re-renders all items when list changes
    // With trackBy: Only re-enders changed items
    
    trackByFn(index: number, item: User): number {
        return item.id; // Returns unique identifier
    }

    // Alternative: Using aeeow function
    trackByFn = (index: number, item: User) => item.id;
  -->

<!-- Angular 15+ :new Syntex -->
 <!-- basic @for -->
@for(item of items track; item.id) { }
<!-- index @for -->
@for(item of items track;  let i=$index; track item.id) { }
<!-- odd, even @for -->
@for(item of items track; let first=$first; let last=$last track user.id) { }
 <!-- Empaty state with @Empty -->
@for(item of items track; item.id) { 

} @Empty{
    <p>No item is available</p>
}
<!-- ngSwitch -->
 <div [ngSwitch]="status">
    <div *ngSwitchCase="'active'">
    <div *ngSwitchDefault>

<!-- ngTemplateOutlate  -> Randers a template dynamically-->
 <ng-container *ngTemplateOutlate="haderTemplate"></ng-container>
 <ng-container *ngTemplateOutlate="contentTemplate"></ng-container>

 <ng-template #haderTemplate>Hraders Template</ng-template>
 <ng-template #contentTemplate>Content Template</ng-template>

Note: Benefits of TrackBy:

  • Improve perfomance with large lists
  • Prevents re-rendering unchange items
  • Maintains forms state and focus

Attribute Directives

Modify the appearance or behavior of DOM elements without changing structure.

<div [ngClass]="{ active: isActive }">
<div [ngStyle]="{color: dynamicColor}">
<input [(ngModel)]="name">

difference in ngClass and class Binding

ngClassclass Binding
Multiple class at onceSingle class binding
Conditional class assignmentDirect property binding
Can accept object, string, or arraySimple syntex
Content
Content
Content

When to use

  • ngClass: Multiple condition classes
  • Class binding: Single class or static classes

Custom Directives

Creating Custom Attribute Directives

Key Concept:

  • Implement @Directive decorator
  • Use @Input for properties
  • Use ElementRef to access element
  • Use Randerer2 for safe DOM manipulation

Step 1: Generate directives

ng generate directive directives/highlight
#or
ng g d directives/highlight

Step 2: Implemetnt Custom directive

import { Directive, ElementRef, HostListener, Input } from '@angular/core';

@Directive({
 selector: '[appHighlight]', // Attribute selector: use as <p appHighlight>
 standalone: true // Directives are standalone by default in modern Angular
})
export class HighlightDirective {
 // Define an input property to allow specifying a custom highlight color
 @Input() highlightColor: string = 'yellow';

 // Inject ElementRef to access the host element
 constructor(private el: ElementRef) {}

 // Listen for the mouseenter event on the host element
 @HostListener('mouseenter') onMouseEnter() {
   this.highlight(this.highlightColor);
 }

 // Listen for the mouseleave event on the host element
 @HostListener('mouseleave') onMouseLeave() {
   this.highlight(''); // Clear the highlight
 }

 // Private helper method to apply the style
 private highlight(color: string) {
   this.el.nativeElement.style.backgroundColor = color;
 }
}

Step 3: Use the Directive in a Component

// In Component
imports: [HighlightDirective], // Add the directive to the imports array

// In Template 
<!-- Use the directive with the default yellow color -->
<p appHighlight>Hover over me for default highlight!</p>

<!-- Use the directive and bind an input property for a custom color -->
<p [appHighlight]="favoriteColor">Hover over me for lightblue highlight!</p>

Component/Directive Lifecycle Hook

HookDirectiveComponentPurpuse
constructorInitialize dependencies
ngOnChangeDetect input change
ngOnInitInitialize component/directive
ngDoCheckCustome change detection
ngAfterContentInitAfter content initialization
ngAfterViewInitXAfter view initilazition
ngOnDestroyCleanup

Directive Don't have:

  • ngAfterViewInit - Directives have no view
  • ngAfterViewChecked - Directives have no view

Angular Pipes

  • Pipe are simple functions that accept an input value and return a transformed output value. Key Charaterstics:
  • Transform data in templates without changing the original data
  • Can be chained together
  • Can accept parameters
  • Can be pure or impure
  • Reusable across the application

Built-in Pipes

Angualr provides several built-in pipes for common transformation:

  • DatePipe
  • CurrencyPipe
  • NumberPipe
  • PercentagePipe
  • Uppercase/LowerCase/TitleCasePipe
  • SlicePipe
  • JsonPipe

Custom Pipe

Step 1: Generate Pipe

ng generate pipe pipes/custom-pipe-name
#or 
ng g p pipes/custom-pipe-name

Step 2: Implement Pipe logic

import {Pipe, PipeTrasform } from '@angular/core';

@pipe({
    name: 'reverse',
    standalone: true
})
export class ReversePipe implements Pipetransform {
    transform(value: string): string {
        if(!value) return value;
        return value.split('').reverse().join('');
    }
}

Usage

{{ 'Angular' | reverse }}

Pure vs Impure Pipe

PurePipe

Charateristics:

  • Called only when Angular detects a pure change to the input value
  • Pure changes: primitive value (String, Number, boolean) or object reference
  • More performant as they're colled less frequently
  • Default behaviour (pure: true)

Example

@pipe({
    name: 'purePipe',
    pure: true // Default
})
export class AppComponent{ 
    items = ['Apple', 'Banana', 'Cherry'];

    addItem(){
        // This won't trigger pure pipe (same reference)
        this.items.push('Date');

        //This will trigger pure pipe (new refrence)
        this.items = [...this.item, 'date'];
    }
}

Impure Pipes

Charateristics:

  • Called on every change detection cycle
  • Detects change within objects or array
  • Less performent but more responsive to data change
  • Must explicitly set pure: false
@pipe({
    name: 'purePipe',
    pure: false // Impure pipe
})
export class ImpurePipe implements PipeTrasform{
    transform(item: any[], searchText: string): any[]{
        if(!items || !searchtext) return items;
        return items.filter(item => 
            item.toLowerCase().includes(searchText.toLowerCase())
        );
    }
}

When to Use Impure Pipe

  • Filtering/ Sorting dynamic array
  • Real-time dat transformation
  • When we need to detech changes within object/arrays

Async Pipe

This Async Pipe is a spacial built-in pipe that subscribes to Observables or Promises and return the letest value.

Key Benifits

  1. Automatic subscription Management: Automattically scubscribe and Unsubscribe
  2. Memory Leak Preventation: Prevent memory leaks
  3. Cleaner Code: No need for manual subscription in component

Observable Example:

// Component 
//Observable that emit every second
time$: Observable<Date> = interval(1000).pipe(
  map(()=> new Date())
);
//Template
// <h3> Current Time: {{time$ | async | date: 'medium'}}


//Observable form Http call
user$: Observable<User> = this.http.get<User>('/api/user');

constructor(private http: HttpClient ){}

//Template
// <h3> USer: {{user$ | async | json }}

Promise Example

// Component
dataPromise: Promise<string>;

constructor(){
  this.dataPromise = this.fatchData();
}

fatchData(): Promise<string>{
  return new Promise((resolve)=> {
      setTimeout(()=> resolve('Data loaded!'), 2000);
  });
}

// Template
<div>{{dtaPromise | async }} </div>

Pipe Life Cycle

  • Construction
  • Transform method calls
  • Destruction

OnPush OnPush change detection strategy is a performance optimization that instructs Angular to skip checking a component (and its children) unless specific triggers occur.

import { ChangeDetectionStrategy, Component } from '@angular/core';

@Component({
 selector: 'app-on-push-demo',
 templateUrl: './on-push-demo.component.html',
 changeDetection: ChangeDetectionStrategy.OnPush
})
export class MyComponent { }

Comunication Method in Angular

Type of Communication

TypeDirectionUse Case
@InputParent -> ChildPass sata down the component tree
@OutputChild -> ParentEmit events up the component tree
ServiceAny -> AnyShared data accross components
ViewChildParent -> Child TemplateDirect DOM/Component access
Template Ref VariablesTemplateLocal template access
SubjectAny <-> AnyComplex state management

@Input - Passing data ot Child component (one way data binding)

// Child component ts
import { Component, Input } from '@angular/core';

@Input() message: string ='';
// Input with alias
@Input('message') data: any;
// Required input (Angular 16+)
@Input({ required: true }) title: string;

// parent component ts
ParentMessage =' Hello from Parent';
<app-child [message]="ParentMessage"></app-child>

@Output - Emitting Events from Child t Parent.

//child component ts 
import { Component, Output, EventEmitter} from '@angular/core';

@Output() messageEvent = new EventEmitter<string>();

sentMesage(){
    this.messageEvent.emit('Hello from child!');
}

//Parent component
receivedMessage (message: string){
    console.log(message);
}
<app-child (messageEvent)="receivedMessage($event)">

ViewChild/ViewChildren/ContentChild/ContentChildren

These decorater allow perent component to access child component instances or DOM elements directly.

ViewChild - Single Child Reference

// parent component ts
//-------------------------------
import { Component, ViewChild } from '@angular/core';
import { ChildComponent } from './child-component';

@ViewChild('ChildComponent') child!: ChildComponent;
// OR by template reference variable:
//@ViewChild('ChildRef') childRef!: ChildComponent;

ngAfterViewInit(){
    //child is now available
    console.log(this.child.message); // output: Hello from the child!
}
callChildMethod() {
    this.child.updateMessage('Message updated by parent!');
  }

// parent template
//--------------------- 
<app-child></app-child>
// or
//<app-child #ChildRef></app-child>

// Child Component ts
//--------------------------------
 message = 'Hello from the child!';

  public updateMessage(newMessage: string) {
    this.message = newMessage;
  }

Key Points:

  • @ViewChild is available after ngAfterViewInit()
  • Use for direct child components in the template.
  • Single reference only.

ViewChildren - Multiple Child References

// Parent Template html
<app-child *ngFor="let item of items" #children></app-child>

// Parent component ts
import { AfterViewInit, Component, QueryList, ViewChildren } from '@angular/core';
import { ChildComponent } from './child.component';

@ViewChildren('children') children!: QueryList<ChildComponent>;

ngAfterViewInit() {
    // Access and interact with each child component
    this.children.forEach(child => {
      console.log(child); // Access properties or call methods
      // child.someMethod();
    });

    // Subscribe to changes in the list of children
    this.children.changes.subscribe((newList: QueryList<ChildComponent>) => {
      console.log('Children list updated:', newList.toArray());
    });
}

ContentChild - Access Projected component

Used to access child component/elements that are projected via <ng-content>.

@ContentChild (or its signal-based equivalent, contentChild()) is a decorator used to access the first matching element, component, or directive that has been projected into a component using the directive.

//1. Parent Component- The parent component provides the content to be projected. 

// app.component.html
//------------------------------------
<app-container>
  <app-header><app-header>
</app-container>
// or 
//<app-container #headerRef>
//   <app-header><app-header>
// </app-container>

//2. Child Component (Container) - The child component uses <ng-content> to display the projected content and @ContentChild to access it in its TypeScript class. 

// container.component.ts
//-------------------------------------
import { AfterContentInit, ContentChild, ElementRef, Component } from '@angular/core';

@Component({
  selector: 'app-container',
  template: `
    <div class="container">
      <ng-content></ng-content> <!-- Projection slot -->
    </div>
  `,
})
export class ContainerComponent implements AfterContentInit {
    @ContentChild('HeaderComponent') header!: HeaderComponent;
  // Query for the element with the template reference variable '#headerRef'
 // @ContentChild('headerRef') projectedHeaderEl!: ElementRef;

  ngAfterContentInit() {
    // The projected content is available here
    if (this.projectedHeaderEl) {
        this.heaader.highlight();
    //or
    //   console.log('Projected content text:', this.projectedHeaderEl.nativeElement.textContent);
      // You can also manipulate the DOM here, e.g., change style
     // this.projectedHeaderEl.nativeElement.style.color = 'blue';
    }
  }
}


ContentChildren - Multiple Projected items

import { Component, ContentChildren, QueryList} from '@angular/core';
import { TabComponent } from './tab.component';

@Component({
    selector: 'app-tab-group',
    template: `
        <div class="tab">
            <ng-content></ng-content>
        </div>
        `
})
export class TabGroupComponent implements AfterContentInit {
    
    @ContentChildren(TabComponent) tabs!: QueryList<TabComponent>;

    AfterContentInit(){
        this.tab.forEach((tab, index) => {
            tab.id = index;
        });
    }
}

Comparison Table

DecoratorTargetWhen AvailableUsage
ViewChildDirect child in TemplateAfterViewInitSingle refrence
ViewChildrenMultiple direct childrenAfterViewInitQueryList
ContentChildProjected content childAfterContentInitSingle reference
ContentChildrenMultiple projected childrenAfterContentInitQueryList

Template Reference variables

Template reference variables allow to refrence DOM elements or components in the template.

    //Basic usage
    //-----------------------
    //app component html
    <input #nameInput type="text">
    <button (click)="onSubmit(nameInput.value)">Submit</button>

    // app component ts
    onSubmit(value: string){
        console.log(value);
    }

    //Getting Dom Element Reference
    //-----------------------
    //app component html
    <input #nameElement type="text">
    <button (click)="focusInput()">Focus Input</button>

    // app component ts
    import { ViewChild, ElementRef } from '@angular/core';

    @ViewChild('nameElement') inputElement! = ElementRef<HTMLInputElement>;

    focusInput() {
        this.inputElement.nevigateElement.focus();
    }

    // Template ref with ng-template
    //-------------------------------------
    //app component html
    <div *ngIf="!isloading; else loadingTemplate">
    <ng-template #loadingTemplate>
        <p>Loading....</p>
    </ng-template>

Event Emitter vs Subjects

Both are used for event-driven communication, but they have different use cases and characteristics.

EventEmitter

EventEmitter is a subclass of subject specifically designed for component outputs.

Charaterstics:

  • Part of @angular/core
  • Meant for @Output() decoretors
  • Synchronous by default
  • Limited API comared to Subject

Subject

Subject is RxJS class that act as bot an observable and observer.

Type

  1. Subject:- No Initial value
  2. BehaviourSubject: - Has initial value
  3. ReplaySubject: - Buffer Values
  4. AsyncSubject: - Emits value on Complate
FeatureEventEmitterSubject
Use case@Output decoratorService-based communication
Memory EffcientYesDepends on type
Complate on unsubscribeyesNo
Can Complateyesyes
RxJs operatorLimitedFull RxJS Suport
Asnyc capabilityNoYes(with async operation)

Angular Dependency Injection

DI is a design pattern where a class receives its dependencies from external source rather then creating them itself. Angular's DI framework provides dependencies to class upon instantiation.

  1. Makes code more testable ( can inject mocks)
  2. Promotes loose coupling
  3. Enables singleton pattern for service
  4. Simplify component code
  5. Makes dependencies explicit and manageable.

Benifits:

  • Testability: Easy to mock demendencies in unit tests
  • Maintainability: Loose coupling b/w components
  • Reusability: Service can be shared accross the application.
  • Flexiblility: Easy to swap implementations

DI is differ from manual dependency injection as below:

  • Automatic: framework handles instantiation and injection
  • Hierarchical: Multi-level injector tree
  • Declarative: Use decorators (@Injectable, Inject)
  • Optimized: Tree-shakeable and laz-loadable
  • Type-safe: Leverages Typescript types as tokens

Manual DI requires explicit instatiation and passing of dependencies.

Hierarchical Injector System

a core part of its Dependency Injection (DI) mechanism, creating a tree of injectors that mirrors the application's component tree.

Tree Structure: Every component instance can have its own injector, which is a child of its parent component's injector

Injector Hierarchy levels

NullInjector (top) -> throws error if token not found
        ⇩
PlatformInjector -> Platform level dependencies
        ⇩
RootInjector (Application/ root) -> Singletons Application wise
        ⇩
ModuleInjector (lazy-loaded modules) -> Lazy loaded module scope
        ⇩
ElementInjector (Component/Directive)
        ⇩
ElementInjector (Child component)

Provider Scopes

1. Root Scope

Singleton accross entire application

@Injectable({ provideIn: 'root' })

charaterstics:

  • Tree-shakable ( removed if not used)
  • Lazy instantiation (created when first inject)
  • Single instance app-wide

when use

  • Service manages global state (AuthService, ConfigService)
  • Service should be singleton (HttpClient, Logger) -Service used accross multiple features
  • want tree-shaking benfits

2. Platform Scope

Shared accross multiple Angular application on the same page

@Injectable({ provideIn: 'platform' })

Use case: Rare, typically for microfrontends.

3. Module Scope

Singleton within module

//Eager-loaded module
@NgModule({
    provides: [ModuleService]
})
// or using priovdeIn
@Injectable({ provideIn: 'FeatureMudle' })

4. Component Scope

New instance per component

@Component({
    ....
    providers: [ComponentService] // New instance
})

constructor(private service : ComponentService){}

When use

  • Service manages component specific state
  • Need different instance for different component
  • Service lifecycle tide to omponent
  • want isolation ( FormService for eatch form)

Example:-

  • Root: AuthService, HttpClient, GlobalErrorHandler
  • Component: FormService, LocalFilterService, WidgetStateService

@Injectable() Decorator Options

Comments

Popular posts from this blog