Skip to the solution
MediumFrontend8 min read

What is the difference between @ViewChildren and @ViewChild?

1.2k views

The problem

Question presented to candidate: "You have a list of item components rendered with @for. When would you use @ViewChild and when @ViewChildren to reach them, what type does each give you, and how do you react when items are added or removed?"

What a strong answer should cover:

  • @ViewChild returns the first match (or undefined); @ViewChildren returns all matches as a QueryList.
  • QueryList: length, first, last, toArray(), forEach/map/filter, and the changes observable that emits when the set of matches changes.
  • Both are live for dynamic (@if / @for) content and are first ready in ngAfterViewInit; only @ViewChild has a static option (passing it to @ViewChildren is a type error).
  • Same locators and read option for both; under the hood @ViewChild is the same query with .first taken.
  • Subscribe to changes in ngAfterViewInit and clean up; with signal queries, viewChildren() returns a signal of a readonly array instead.
  • Do not keep a stale reference to first: it can change as items are added or removed.

Clarifying questions expected:

  • "Do I need the component instances or the DOM elements?"
  • "Can the list change at runtime, or is it fixed?"

Code / implementation expected: Yes — a list where one button acts on the @ViewChild result and another on every @ViewChildren result.

  • components
  • queries

Try it yourself

Sketch your approach before you read the solution. That is what the interview tests.

Stuck? Get a hint.

A short nudge toward the approach. It will not give the answer away.

Solution

The solution is hidden so you can try first

8 min read · 9 sections

Read the code

AngularOne vs all: highlight the @ViewChild result or every @ViewChildren result in a live list
import { AfterViewInit, Component, ElementRef, Input, QueryList, ViewChild, ViewChildren, signal } from "@angular/core";

@Component({
  selector: "todo-item",
  template: `<li [class.hot]="hot()">{{ label }}{{ hot() ? " (highlighted)" : "" }}</li>`,
})
export class TodoItemComponent {
  @Input() label = "";
  hot = signal(false);
}

@Component({
  selector: "app-root",
  imports: [TodoItemComponent],
  template: `
    <ul>
      @for (t of todos(); track t) { <todo-item #item [label]="t" /> }
    </ul>
    <button (click)="first.hot.set(true)">highlight @ViewChild</button>
    <button (click)="highlightAll()">highlight @ViewChildren</button>
    <button (click)="todos.set([...todos(), 'item ' + (todos().length + 1)])">add item</button>
    <p>{{ status() }}</p>`,
})
export class AppComponent implements AfterViewInit {
  todos = signal(["item 1", "item 2"]);
  status = signal("");

  // ONE result: the first match in template order.
  @ViewChild(TodoItemComponent) first!: TodoItemComponent;
  // ALL results, as a live QueryList. Same locator, different shape.
  @ViewChildren(TodoItemComponent) items!: QueryList<TodoItemComponent>;
  // read: change WHAT you get back for each match (here the host element).
  @ViewChildren("item", { read: ElementRef }) hosts!: QueryList<ElementRef<HTMLElement>>;

  ngAfterViewInit() {
    this.report("ngAfterViewInit");
    console.log("read: ElementRef ->", this.hosts.map((h) => h.nativeElement.tagName.toLowerCase()).join(", "));
    // QueryList.changes emits when the set of matches changes.
    this.items.changes.subscribe(() => this.report("items.changes"));
  }

  highlightAll() {
    this.items.forEach((item) => item.hot.set(true));
  }

  private report(where: string) {
    this.status.set(`${where}: ${this.items.length} items, first = "${this.first.label}", last = "${this.items.last.label}"`);
  }
}

Discussion

Other approaches, follow-ups and how it went in real interviews.

Sign in to join the discussion.

No comments yet. Share how you would answer it.

Question 14 of 121 in Angular

Keep your momentum going.

Mark this question solved to track your progress, then take the next one while it is fresh.

All Angular questions
Up nextMedium

What is a route resolver and when is it useful?

Continue