Angular Components Cheat Sheet: Complete Guide to Component Architecture

Component Basics

Component Structure

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

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css']
})
export class MyComponent {
  // Component properties and methods
}

Creating Components

# Generate a new component
ng generate component my-component
# Shorthand
ng g c my-component

# Create in specific folder
ng g c features/user/user-profile

# With inline template
ng g c my-component --inline-template
# or
ng g c my-component -t

# With inline styles
ng g c my-component --inline-style
# or
ng g c my-component -s

# Without test file
ng g c my-component --skip-tests

# Standalone component (Angular 14+)
ng g c my-component --standalone

Component Selector Types

@Component({
  // Element selector (most common)
  selector: 'app-my-component',
  
  // Attribute selector
  selector: '[app-my-component]',
  
  // Class selector
  selector: '.app-my-component'
})

Usage in templates:

<!-- Element selector -->
<app-my-component></app-my-component>

<!-- Attribute selector -->
<div app-my-component></div>

<!-- Class selector -->
<div class="app-my-component"></div>

Inline Templates and Styles

@Component({
  selector: 'app-my-component',
  // Inline template
  template: `
    <h1>{{ title }}</h1>
    <p>This is an inline template</p>
  `,
  // Inline styles
  styles: [`
    h1 { color: blue; }
    p { font-size: 16px; }
  `]
})

Component Data Binding

Interpolation

<h1>{{ title }}</h1>
<p>Welcome, {{ user.name }}</p>
<div>{{ getFullName() }}</div>
<span>{{ 2 + 2 }}</span>

Property Binding

<!-- Binding to element property -->
<img [src]="imageUrl">
<button [disabled]="isDisabled">Click me</button>

<!-- Binding to a custom property of a component -->
<app-child [data]="parentData"></app-child>

<!-- Alternative syntax (canonical form) -->
<img bind-src="imageUrl">

Event Binding

<!-- Basic event binding -->
<button (click)="onClick()">Click me</button>

<!-- With event object -->
<input (keyup)="onKeyUp($event)">
<form (submit)="onSubmit($event)">

<!-- Alternative syntax (canonical form) -->
<button on-click="onClick()">Click me</button>

Two-way Binding

<!-- Using ngModel (requires FormsModule) -->
<input [(ngModel)]="name">

<!-- "Banana in a box" syntax -->
<app-counter [(count)]="parentCount"></app-counter>

<!-- Alternative syntax (canonical form) -->
<input bindon-ngModel="name">

<!-- Expanded form (equivalent to ngModel) -->
<input [ngModel]="name" (ngModelChange)="name = $event">

Component Lifecycle Hooks

import {
  Component, OnInit, OnChanges, DoCheck, AfterContentInit,
  AfterContentChecked, AfterViewInit, AfterViewChecked,
  OnDestroy, SimpleChanges
} from '@angular/core';

@Component({...})
export class LifecycleComponent implements 
  OnInit, OnChanges, DoCheck, AfterContentInit,
  AfterContentChecked, AfterViewInit, AfterViewChecked, OnDestroy {
  
  // Called before ngOnInit and when input properties change
  ngOnChanges(changes: SimpleChanges): void {
    console.log('ngOnChanges', changes);
  }
  
  // Called once after the first ngOnChanges
  ngOnInit(): void {
    console.log('ngOnInit');
    // Ideal for initialization logic, API calls
  }
  
  // Called during every change detection cycle
  ngDoCheck(): void {
    console.log('ngDoCheck');
  }
  
  // Called after content projection is initialized
  ngAfterContentInit(): void {
    console.log('ngAfterContentInit');
  }
  
  // Called after every check of projected content
  ngAfterContentChecked(): void {
    console.log('ngAfterContentChecked');
  }
  
  // Called after component's view is initialized
  ngAfterViewInit(): void {
    console.log('ngAfterViewInit');
    // Safe to interact with view DOM elements
  }
  
  // Called after every check of component's view
  ngAfterViewChecked(): void {
    console.log('ngAfterViewChecked');
  }
  
  // Called before component is destroyed
  ngOnDestroy(): void {
    console.log('ngOnDestroy');
    // Cleanup: unsubscribe from observables, clear timers
  }
}

Lifecycle Execution Order

  1. ngOnChanges – First and when input properties change
  2. ngOnInit – Once after first ngOnChanges
  3. ngDoCheck – After ngOnChanges and ngOnInit, and during every change detection
  4. ngAfterContentInit – After projected content is initialized
  5. ngAfterContentChecked – After content check
  6. ngAfterViewInit – After component’s view initialization
  7. ngAfterViewChecked – After view check
  8. ngOnDestroy – Before component destruction

Component Communication

Parent to Child: @Input

Child component:

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

@Component({
  selector: 'app-child',
  template: `<p>Message from parent: {{ message }}</p>`
})
export class ChildComponent {
  @Input() message: string = '';
  
  // With alias
  @Input('userData') user: any;
  
  // With setter for intercepting changes
  private _count = 0;
  
  @Input()
  set count(value: number) {
    console.log(`Count changed from ${this._count} to ${value}`);
    this._count = value;
  }
  
  get count(): number {
    return this._count;
  }
  
  // With OnChanges hook
  @Input() data: any;
  ngOnChanges(changes: SimpleChanges) {
    if (changes['data']) {
      console.log('Previous:', changes['data'].previousValue);
      console.log('Current:', changes['data'].currentValue);
      console.log('First change:', changes['data'].firstChange);
    }
  }
}

Parent component:

@Component({
  selector: 'app-parent',
  template: `
    <app-child 
      [message]="parentMessage"
      [userData]="user"
      [count]="counter"
      [data]="parentData">
    </app-child>
    <button (click)="updateData()">Update Data</button>
  `
})
export class ParentComponent {
  parentMessage = 'Hello from parent';
  user = { name: 'John', age: 30 };
  counter = 0;
  parentData = { value: 'initial' };
  
  updateData() {
    this.counter++;
    this.parentData = { value: 'updated' };
  }
}

Child to Parent: @Output and EventEmitter

Child component:

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

@Component({
  selector: 'app-child',
  template: `
    <button (click)="sendMessage()">Send to Parent</button>
    <button (click)="incrementCounter()">Increment</button>
  `
})
export class ChildComponent {
  // Basic event emitter
  @Output() messageEvent = new EventEmitter<string>();
  
  // With alias
  @Output('counterChange') countChanged = new EventEmitter<number>();
  
  // Event with object data
  @Output() userEvent = new EventEmitter<{name: string, action: string}>();
  
  private count = 0;
  
  sendMessage() {
    this.messageEvent.emit('Hello from child');
  }
  
  incrementCounter() {
    this.count++;
    this.countChanged.emit(this.count);
  }
  
  sendUserAction(action: string) {
    this.userEvent.emit({name: 'User', action: action});
  }
}

Parent component:

@Component({
  selector: 'app-parent',
  template: `
    <p>Message from child: {{ message }}</p>
    <p>Counter value: {{ counter }}</p>
    <p>User action: {{ userAction }}</p>
    
    <app-child 
      (messageEvent)="receiveMessage($event)"
      (counterChange)="updateCounter($event)"
      (userEvent)="handleUserEvent($event)">
    </app-child>
  `
})
export class ParentComponent {
  message = '';
  counter = 0;
  userAction = '';
  
  receiveMessage(msg: string) {
    this.message = msg;
  }
  
  updateCounter(count: number) {
    this.counter = count;
  }
  
  handleUserEvent(event: {name: string, action: string}) {
    this.userAction = `${event.name} performed ${event.action}`;
  }
}

Two-way Binding Custom Properties

Child component:

import { Component, Input, Output, EventEmitter } from '@angular/core';

@Component({
  selector: 'app-counter',
  template: `
    <div>
      <button (click)="decrement()">-</button>
      <span>{{ value }}</span>
      <button (click)="increment()">+</button>
    </div>
  `
})
export class CounterComponent {
  @Input() value: number = 0;
  @Output() valueChange = new EventEmitter<number>();
  
  increment() {
    this.value++;
    this.valueChange.emit(this.value);
  }
  
  decrement() {
    this.value--;
    this.valueChange.emit(this.value);
  }
}

Parent component:

@Component({
  selector: 'app-parent',
  template: `
    <p>Counter: {{ count }}</p>
    <app-counter [(value)]="count"></app-counter>
  `
})
export class ParentComponent {
  count = 0;
}

Parent-Child Interaction via Template Reference

Parent template:

<div>
  <app-child #childComponent></app-child>
  <button (click)="childComponent.doSomething()">Call Child Method</button>
</div>

Child component:

@Component({
  selector: 'app-child',
  template: `<p>Child component</p>`
})
export class ChildComponent {
  doSomething() {
    console.log('Child method called from parent');
  }
}

Accessing Child Components with @ViewChild

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

@Component({
  selector: 'app-parent',
  template: `
    <app-child></app-child>
    <button (click)="callChildMethod()">Call Child Method</button>
  `
})
export class ParentComponent implements AfterViewInit {
  @ViewChild(ChildComponent) childComponent!: ChildComponent;
  
  // With template reference variable
  @ViewChild('childRef') childWithRef!: ChildComponent;
  
  // Static query - available in ngOnInit
  @ViewChild(ChildComponent, { static: true }) 
  childComponentStatic!: ChildComponent;
  
  ngAfterViewInit() {
    // Access child after view is initialized
    this.childComponent.doSomething();
  }
  
  callChildMethod() {
    this.childComponent.doSomething();
  }
}

Accessing Multiple Children with @ViewChildren

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

@Component({
  selector: 'app-parent',
  template: `
    <app-child *ngFor="let i of [1,2,3]"></app-child>
    <button (click)="callAllChildMethods()">Call All Child Methods</button>
  `
})
export class ParentComponent implements AfterViewInit {
  @ViewChildren(ChildComponent) 
  childrenComponents!: QueryList<ChildComponent>;
  
  ngAfterViewInit() {
    this.childrenComponents.forEach(child => {
      child.doSomething();
    });
  }
  
  callAllChildMethods() {
    this.childrenComponents.forEach(child => {
      child.doSomething();
    });
  }
}

Content Projection (ng-content)

Child component (with content projection):

@Component({
  selector: 'app-card',
  template: `
    <div class="card">
      <div class="card-header">
        <ng-content select="[card-header]"></ng-content>
      </div>
      <div class="card-body">
        <ng-content></ng-content>
      </div>
      <div class="card-footer">
        <ng-content select="[card-footer]"></ng-content>
      </div>
    </div>
  `,
  styles: [`
    .card {
      border: 1px solid #ccc;
      border-radius: 4px;
      margin-bottom: 10px;
    }
    .card-header {
      background-color: #f5f5f5;
      padding: 10px;
      border-bottom: 1px solid #ccc;
    }
    .card-body {
      padding: 10px;
    }
    .card-footer {
      background-color: #f5f5f5;
      padding: 10px;
      border-top: 1px solid #ccc;
    }
  `]
})
export class CardComponent { }

Parent component (using content projection):

<app-card>
  <div card-header>
    <h3>Card Title</h3>
  </div>
  
  <p>This is the main content of the card.</p>
  <p>Multiple elements can be projected.</p>
  
  <div card-footer>
    <button>Action</button>
  </div>
</app-card>

Accessing Projected Content with @ContentChild

import { Component, ContentChild, AfterContentInit } from '@angular/core';
import { ProjectedComponent } from './projected.component';

@Component({
  selector: 'app-wrapper',
  template: `
    <div class="wrapper">
      <ng-content></ng-content>
    </div>
    <button (click)="accessProjectedContent()">
      Access Projected Content
    </button>
  `
})
export class WrapperComponent implements AfterContentInit {
  @ContentChild(ProjectedComponent) 
  projectedComponent!: ProjectedComponent;
  
  // With template reference variable
  @ContentChild('projectedRef') 
  projectedComponentWithRef!: ProjectedComponent;
  
  ngAfterContentInit() {
    // Access projected content after it's initialized
    if (this.projectedComponent) {
      console.log('Projected content initialized');
      this.projectedComponent.doSomething();
    }
  }
  
  accessProjectedContent() {
    if (this.projectedComponent) {
      this.projectedComponent.doSomething();
    }
  }
}

Parent component:

<app-wrapper>
  <app-projected #projectedRef></app-projected>
</app-wrapper>

Accessing Multiple Projected Contents with @ContentChildren

import { Component, ContentChildren, QueryList, AfterContentInit } from '@angular/core';
import { ProjectedItemComponent } from './projected-item.component';

@Component({
  selector: 'app-list-wrapper',
  template: `
    <div class="list-wrapper">
      <ng-content></ng-content>
    </div>
    <button (click)="logAllItems()">Log All Items</button>
  `
})
export class ListWrapperComponent implements AfterContentInit {
  @ContentChildren(ProjectedItemComponent) 
  projectedItems!: QueryList<ProjectedItemComponent>;
  
  ngAfterContentInit() {
    console.log(`Projected ${this.projectedItems.length} items`);
    this.projectedItems.forEach(item => {
      console.log(item.data);
    });
  }
  
  logAllItems() {
    this.projectedItems.forEach(item => {
      console.log(item.data);
    });
  }
}

Parent component:

<app-list-wrapper>
  <app-projected-item [data]="'Item 1'"></app-projected-item>
  <app-projected-item [data]="'Item 2'"></app-projected-item>
  <app-projected-item [data]="'Item 3'"></app-projected-item>
</app-list-wrapper>

Component Styles

Style Encapsulation Modes

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

@Component({
  selector: 'app-my-component',
  templateUrl: './my-component.component.html',
  styleUrls: ['./my-component.component.css'],
  
  // ViewEncapsulation options:
  
  // Emulated (default) - styles are scoped to component
  encapsulation: ViewEncapsulation.Emulated,
  
  // None - styles leak out to global scope
  // encapsulation: ViewEncapsulation.None,
  
  // ShadowDom - uses browser's native Shadow DOM
  // encapsulation: ViewEncapsulation.ShadowDom
})

Special Selectors

/* styles.css (component stylesheet) */

/* :host - targets the element hosting the component */
:host {
  display: block;
  margin: 10px;
}

/* :host with condition */
:host(.active) {
  border: 2px solid blue;
}

/* :host-context - applies styles when some ancestor has a CSS class */
:host-context(.theme-dark) {
  background-color: #333;
  color: white;
}

/* ::ng-deep - forces styles to apply to child components */
/* Use carefully - will be deprecated eventually */
:host ::ng-deep .child-element {
  color: red;
}

Dynamic Components

Creating Components Dynamically

import {
  Component, ViewChild, ViewContainerRef,
  ComponentFactoryResolver, ComponentRef
} from '@angular/core';
import { DynamicComponent } from './dynamic.component';

@Component({
  selector: 'app-dynamic-host',
  template: `
    <div>
      <ng-container #container></ng-container>
      <button (click)="createComponent()">Create Component</button>
      <button (click)="removeComponent()">Remove Component</button>
    </div>
  `
})
export class DynamicHostComponent {
  @ViewChild('container', { read: ViewContainerRef }) 
  container!: ViewContainerRef;
  
  private componentRef: ComponentRef<DynamicComponent> | null = null;
  
  constructor(private cfr: ComponentFactoryResolver) { }
  
  createComponent() {
    // Clear container first
    this.container.clear();
    
    // Create component factory
    const factory = this.cfr.resolveComponentFactory(DynamicComponent);
    
    // Create component
    this.componentRef = this.container.createComponent(factory);
    
    // Set inputs
    this.componentRef.instance.data = 'Dynamic data';
    
    // Subscribe to outputs
    this.componentRef.instance.action.subscribe((data: string) => {
      console.log('Dynamic component action:', data);
    });
  }
  
  removeComponent() {
    if (this.componentRef) {
      this.componentRef.destroy();
      this.componentRef = null;
    }
  }
}

Alternative Approach in Angular 13+

import { Component, ViewChild, ViewContainerRef } from '@angular/core';
import { DynamicComponent } from './dynamic.component';

@Component({
  selector: 'app-dynamic-host',
  template: `
    <div>
      <ng-container #container></ng-container>
      <button (click)="createComponent()">Create Component</button>
      <button (click)="removeComponent()">Remove Component</button>
    </div>
  `
})
export class DynamicHostComponent {
  @ViewChild('container', { read: ViewContainerRef }) 
  container!: ViewContainerRef;
  
  private componentRef: any = null;
  
  createComponent() {
    // Clear container first
    this.container.clear();
    
    // Create component directly (Angular 13+)
    this.componentRef = this.container.createComponent(DynamicComponent);
    
    // Set inputs
    this.componentRef.instance.data = 'Dynamic data';
    
    // Subscribe to outputs
    this.componentRef.instance.action.subscribe((data: string) => {
      console.log('Dynamic component action:', data);
    });
  }
  
  removeComponent() {
    if (this.componentRef) {
      this.componentRef.destroy();
      this.componentRef = null;
    }
  }
}

Standalone Components (Angular 14+)

Creating a Standalone Component

import { Component } from '@angular/core';
import { CommonModule } from '@angular/common';
import { AnotherStandaloneComponent } from './another-standalone.component';

@Component({
  selector: 'app-standalone',
  standalone: true,
  imports: [
    CommonModule,
    AnotherStandaloneComponent
  ],
  template: `
    <h1>{{ title }}</h1>
    <app-another-standalone></app-another-standalone>
  `
})
export class StandaloneComponent {
  title = 'I am a standalone component';
}

Using Standalone Components in NgModule

import { NgModule } from '@angular/core';
import { StandaloneComponent } from './standalone.component';

@NgModule({
  imports: [
    StandaloneComponent
  ],
  exports: [
    StandaloneComponent
  ]
})
export class FeatureModule { }

Bootstrapping with Standalone Component

// main.ts
import { bootstrapApplication } from '@angular/platform-browser';
import { AppComponent } from './app/app.component';
import { provideRouter } from '@angular/router';
import { routes } from './app/app.routes';

bootstrapApplication(AppComponent, {
  providers: [
    provideRouter(routes)
  ]
}).catch(err => console.error(err));

Change Detection

OnPush Change Detection

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

@Component({
  selector: 'app-optimized',
  templateUrl: './optimized.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class OptimizedComponent {
  @Input() data: any;
  
  // Component is re-rendered only when:
  // 1. @Input reference changes (not properties inside the object)
  // 2. Event originates from the component or its children
  // 3. Using async pipe which emits new value
  // 4. Manually triggering change detection
}

Manually Triggering Change Detection

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

@Component({
  selector: 'app-optimized',
  templateUrl: './optimized.component.html',
  changeDetection: ChangeDetectionStrategy.OnPush
})
export class OptimizedComponent {
  data = { value: 'initial' };
  
  constructor(private cdr: ChangeDetectorRef) {}
  
  updateWithoutDetection() {
    // This won't update the view with OnPush
    this.data.value = 'updated';
  }
  
  updateWithDetection() {
    // Update data
    this.data.value = 'updated';
    
    // Manually mark for check
    this.cdr.markForCheck();
  }
  
  forceDetection() {
    // Force a change detection cycle
    this.cdr.detectChanges();
  }
  
  detachAndReattach() {
    // Detach from change detection
    this.cdr.detach();
    
    // Make changes
    this.data.value = 'updated while detached';
    
    // Reattach to change detection
    this.cdr.reattach();
  }
}

Component Templates

Structural Directives in Components

@Component({
  selector: 'app-conditional',
  template: `
    <!-- ngIf -->
    <div *ngIf="isVisible; else notVisible">Content is visible</div>
    <ng-template #notVisible>Content is not visible</ng-template>
    
    <!-- ngFor -->
    <ul>
      <li *ngFor="let item of items; let i = index; trackBy: trackById">
        {{ i }}: {{ item.name }}
      </li>
    </ul>
    
    <!-- ngSwitch -->
    <div [ngSwitch]="status">
      <div *ngSwitchCase="'active'">Active content</div>
      <div *ngSwitchCase="'inactive'">Inactive content</div>
      <div *ngSwitchDefault>Unknown status</div>
    </div>
  `
})
export class ConditionalComponent {
  isVisible = true;
  items = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' },
    { id: 3, name: 'Item 3' }
  ];
  status = 'active';
  
  trackById(index: number, item: any): number {
    return item.id;
  }
}

Template Reference Variables

@Component({
  selector: 'app-template-refs',
  template: `
    <!-- Reference to DOM element -->
    <input #nameInput type="text">
    <button (click)="greet(nameInput.value)">Greet</button>
    
    <!-- Reference to component -->
    <app-child #childComp></app-child>
    <button (click)="childComp.doSomething()">Call Child Method</button>
    
    <!-- Reference to directive -->
    <div #ngIfRef *ngIf="show">This is conditionally shown</div>
    <button (click)="show = !show">Toggle</button>
  `
})
export class TemplateRefsComponent {
  show = true;
  
  greet(name: string) {
    alert(`Hello, ${name}!`);
  }
}

Using ng-template and ng-container

@Component({
  selector: 'app-templates',
  template: `
    <!-- ng-template - template that doesn't render by default -->
    <ng-template #myTemplate>
      <p>Template content</p>
    </ng-template>
    
    <!-- Use template programmatically -->
    <button (click)="showTemplate()">Show Template</button>
    
    <!-- Container to hold template content -->
    <ng-container #container></ng-container>
    
    <!-- ng-container with structural directive -->
    <ng-container *ngIf="isVisible">
      <h2>Title</h2>
      <p>Description</p>
    </ng-container>
    
    <!-- Cleaner ngFor with ng-container -->
    <table>
      <tbody>
        <ng-container *ngFor="let item of items">
          <tr>
            <td>{{ item.name }}</td>
          </tr>
          <tr>
            <td>{{ item.description }}</td>
          </tr>
        </ng-container>
      </tbody>
    </table>
  `
})
export class TemplatesComponent implements AfterViewInit {
  @ViewChild('myTemplate') myTemplate!: TemplateRef<any>;
  @ViewChild('container', { read: ViewContainerRef }) container!: ViewContainerRef;
  
  isVisible = true;
  items = [
    { name: 'Item 1', description: 'Description 1' },
    { name: 'Item 2', description: 'Description 2' }
  ];
  
  ngAfterViewInit() {
    // Template is not rendered initially
  }
  
  showTemplate() {
    this.container.clear();
    this.container.createEmbeddedView(this.myTemplate);
  }
}

Contextual Template Variables

@Component({
  selector: 'app-context',
  template: `
    <!-- Template with context -->
    <ng-template #greet let-name="name" let-message="message">
      <h3>Hello, {{ name }}!</h3>
      <p>{{ message }}</p>
    </ng-template>
    
    <!-- Using template with ngTemplateOutlet -->
    <ng-container 
      [ngTemplateOutlet]="greet"
      [ngTemplateOutletContext]="{ name: 'John', message: 'Welcome!' }">
    </ng-container>
    
    <!-- Using it again with different context -->
    <ng-container 
      [ngTemplateOutlet]="greet"
      [ngTemplateOutletContext]="{ name: 'Jane', message: 'Nice to see you!' }">
    </ng-container>
  `
})
export class ContextComponent { }

Component Best Practices

Component Organization

src/app/
├── core/                  # Singleton services, app-level components
│   ├── header/
│   ├── footer/
│   └── core.module.ts
├── shared/                # Reusable components, directives, pipes
│   ├── components/
│   ├── directives/
│   ├── pipes/
│   └── shared.module.ts
└── features/              # Feature modules and components
    ├── feature1/
    │   ├── components/
    │   ├── services/
    │   └── feature1.module.ts
    └── feature2/

Component Design Guidelines

  1. Single Responsibility: Each component should do one thing well
  2. Small Components: Keep components small and focused
  3. Smart vs. Presentational:
    • Smart components: Manage state, fetch data, contain logic
    • Presentational components: Display data, emit events
  4. OnPush Change Detection: Use for better performance
  5. Proper Unsubscription: Clean up subscriptions in ngOnDestroy
  6. Use Interfaces: Define interfaces for input and output data
  7. Component Selectors: Use prefix and meaningful names
  8. Avoid Logic in Templates: Keep complex logic in component class
  9. Avoid Direct DOM Manipulation: Use Angular’s binding system
  10. Input Setters for Side Effects: Use setters for reacting to input changes

Performance Tips

  1. Use trackBy with ngFor:
<div *ngFor="let item of items; trackBy: trackByFn">
  {{ item.name }}
</div>
trackByFn(index: number, item: any): number {
  return item.id; // Unique identifier
}
  1. OnPush Change Detection

  2. Pure Pipes instead of Computed Properties:

// Instead of a getter
get filteredItems() {
  return this.items.filter(item => item.active);
}

// Use a pure pipe
@Pipe({
  name: 'filterItems',
  pure: true
})
export class FilterItemsPipe implements PipeTransform {
  transform(items: any[], isActive: boolean): any[] {
    return items.filter(item => item.active === isActive);
  }
}
  1. Lazy Load Components:

    • Use lazy loading with RouterModule for feature modules
    • Consider using dynamic imports for standalone components
  2. Use async pipe:

<div *ngFor="let item of items$ | async">
  {{ item.name }}
</div>

Resources

Scroll to Top