Chapter 11.2โ˜• 15 min read

ViewChild and ContentChild

ViewChild gives parent access to its child components and DOM elements. ContentChild gives access to projected content. New signal-based APIs make both simpler.

01What is ViewChild

ViewChild is a decorator (and now a function) that gives a parent component access to its children โ€” child components, directives, or DOM elements.

"ViewChild = CCTV camera โ€” parent se child ko dekho aur control karo."

Use cases:

  • Call a child component's public method
  • Read a child component's public property
  • Access a DOM element (input focus, scroll position)
  • Access a directive's API

Two APIs: Old @ViewChild() decorator (always worked) and new viewChild() signal function (Angular 17+). Both work, but the signal version is cleaner.

ViewChild vs ContentChild โ€” key difference:

  • ViewChild โ€” looks INSIDE the component's own template
  • ContentChild โ€” looks at content PROJECTED from parent via ng-content
02ViewChild with Child Component

Access a child component to call its methods or read its properties.

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

@Component({...})
export class ChildComponent {
  greet(name: string) {
    return `Hello ${name} from child!`;
  }

  get status() {
    return 'Child is ready ๐Ÿš€';
  }
}

// PARENT
@Component({...})
export class ParentComponent implements AfterViewInit {
  @ViewChild(ChildComponent) childComp!: ChildComponent;

  ngAfterViewInit() {
    // ViewChild is only available AFTER view init!
    console.log(this.childComp.status);     // 'Child is ready ๐Ÿš€'
    console.log(this.childComp.greet('Bhai')); // 'Hello Bhai from child!'
  }

  callChildMethod() {
    this.childComp.greet('User');
  }
}

"Child ke public methods call karo โ€” parent se remote control."

IMPORTANT timing rules:

  • ngAfterViewInit โ€” first time ViewChild is available
  • In constructor โ€” undefined (component not created yet)
  • In ngOnInit โ€” undefined (view not initialized yet)
  • ngAfterViewChecked โ€” available and updated

Reference by template variable:

// Template: <app-child #theChild>
@ViewChild('theChild') childComp!: ChildComponent;
// Same result, accessed by template variable name
03ViewChild with DOM Element

Access DOM elements directly with ViewChild.

import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';

@Component({
  template: `
    <input #searchInput type="text" placeholder="Search biryanis...">
    <button (click)="focusInput()">Focus</button>

    <div #scrollContainer class="scroll-box">
      @for (item of items; track $index) {
        <p>{{ item }}</p>
      }
    </div>
  `
})
export class SearchComponent implements AfterViewInit {
  @ViewChild('searchInput') searchInput!: ElementRef<HTMLInputElement>;
  @ViewChild('scrollContainer') scrollContainer!: ElementRef<HTMLDivElement>;

  ngAfterViewInit() {
    this.searchInput.nativeElement.focus(); // Auto-focus on load
  }

  focusInput() {
    this.searchInput.nativeElement.focus();
    this.searchInput.nativeElement.value = 'Biryani';
  }

  scrollToTop() {
    this.scrollContainer.nativeElement.scrollTop = 0;
  }
}

"DOM element ko naam do (#myInput), ViewChild se access karo."

โš ๏ธ SSR warning: nativeElement doesn't exist on the server. For SSR-safe code, use Renderer2 or check if platform is browser:

import { isPlatformBrowser } from '@angular/common';
import { PLATFORM_ID, inject } from '@angular/core';

const platformId = inject(PLATFORM_ID);
if (isPlatformBrowser(platformId)) {
  this.searchInput.nativeElement.focus();
}
04ContentChild โ€” Projected Content

ContentChild accesses content that the parent projects into the child via ng-content.

// CHILD COMPONENT
@Component({
  selector: 'app-card',
  template: `
    <div class="card">
      <div class="header">
        <ng-content select="[header]"></ng-content>
      </div>
      <div class="body">
        <ng-content></ng-content>
      </div>
    </div>
  `
})
export class CardComponent {
  @ContentChild('headerTpl') headerTpl!: TemplateRef<any>;
  @ContentChildren(TabComponent) tabs!: QueryList<TabComponent>;

  ngAfterContentInit() {
    console.log('Tabs count:', this.tabs.length);
  }
}

// PARENT TEMPLATE
<app-card>
  <ng-template #headerTpl>
    <h2>๐Ÿ”ฅ Special Biryani</h2>
  </ng-template>
  <p>This is the main content projected via ng-content.</p>
</app-card>

"ContentChild = ng-content ke andar ka content access karo."

ViewChild vs ContentChild timing:

  • ViewChild available in ngAfterViewInit
  • ContentChild available in ngAfterContentInit
  • ContentChildren always INIT before ViewChildren

@ContentChildren (plural) gets ALL matching content children as a QueryList that updates dynamically.

05New Way โ€” viewChild() Signal (Angular 17+)

Angular 17+ introduced signal-based versions of ViewChild and ContentChild.

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

@Component({...})
export class ParentComponent {
  // NEW: viewChild() returns a Signal
  child = viewChild(ChildComponent);
  searchInput = viewChild<ElementRef<HTMLInputElement>>('searchInput');
  headerTpl = contentChild<TemplateRef<any>>('headerTpl');

  // Read the value โ€” just call it like any signal!
  ngAfterViewInit() {
    // OLD: this.childComp.greet('User');
    // NEW:
    this.child()?.greet('User');
    this.searchInput()?.nativeElement.focus();
  }

  // In template โ€” can use directly!
  // {{ child()?.status }}
  // {{ headerTpl() }}
}

"viewChild() = ViewChild ka signal version โ€” modern aur clean."

Benefits over decorator version:

  • No AfterViewInit lifecycle hook required
  • Value is a Signal โ€” can be used in computed(), effect()
  • Auto-updates when child is conditionally shown/hidden (@if)
  • No ! non-null assertion needed
  • Template can read it directly: {{ child()?.status }}

Comparison:

// OLD โ€” @ViewChild decorator
@ViewChild(ChildComponent) child!: ChildComponent;
// Need AfterViewInit to use it

// NEW โ€” viewChild() signal
child = viewChild(ChildComponent);
// Use child() anywhere โ€” signal ecosystem

Key Takeaways

  • โœ… ViewChild = access child components, DOM elements from parent
  • โœ… Available in ngAfterViewInit โ€” NOT in constructor or ngOnInit
  • โœ… ContentChild = access projected content from parent (ng-content)
  • โœ… viewChild() signal (new) โ€” no lifecycle hook needed, signal-powered
  • โœ… contentChild() signal (new) โ€” same benefits for projected content
  • โœ… nativeElement breaks SSR โ€” use Renderer2 or isPlatformBrowser check
Course Search
Search across all chapters & stages
๐Ÿ“–

Search the course

Type any topic โ€” branching, stash, rebase, hooks โ€” and jump straight to that chapter.

merge branchesgit stashundo commitrebase