AcademyAngular
Template Refs, ViewChild and Outlets
#ref names an element for the template; @ViewChild reaches the same element from the class. ng-template and ngTemplateOutlet turn a template into a value.
2 min readUpdated 3 exercises
#ref is scoped to the template; @ViewChild reaches it from the class
A template reference variable, #input, is only visible inside the template that declares it — using it from a sibling handler works because Angular passes it as an argument, not because it leaked into the component's scope. To reach the underlying element or component instance from TypeScript, the class needs @ViewChild, and the two names have to actually match:
@ViewChild('nameInput') nameInputRef!: ElementRef<HTMLInputElement>;
focus(): void {
this.nameInputRef.nativeElement.focus();
}A focus button that silently does nothing is almost always @ViewChild('wrongName') pointing at a #template variable spelled differently in the markup — TypeScript has no way to check that the string argument matches an actual template reference, so the mismatch compiles cleanly and fails only at runtime, as undefined.
ViewChild resolves after the view is initialized, not before
Reading this.nameInputRef inside the constructor or ngOnInit is too early for anything content-projected or conditionally rendered — the reference is only guaranteed to be set by ngAfterViewInit. A character counter "stuck at 0" often traces back to code that read the ref's value once, at the wrong lifecycle moment, instead of subscribing to the input's own events or reading the ref lazily when the counter actually needs it.
ng-template holds markup without rendering it; ngTemplateOutlet renders it, with context
<ng-template> declares a chunk of template that produces nothing on its own — it is inert until something outlets it:
<ng-template #row let-item="item">
<li>{{ item.name }}</li>
</ng-template>
<ng-container *ngTemplateOutlet="row; context: { item: currentItem }" />A reusable list component that accepts a caller-supplied #row template but renders it blank is usually missing the context object entirely, or passing the item under a different key than the template's let-item expects — the template renders, but every {{ item.name }} inside it resolves against undefined.
What interviewers check
Whether you know @ViewChild resolves after ngAfterViewInit, not before, and whether you can explain ngTemplateOutlet's context object as how a caller-supplied template receives data at all — it is easy to use ng-template correctly by copying an example and never understand why the context object is there until it is missing.
Now practice it
1 quiz · 2 drillsReading this page is the cheap half. These are the exercises that make you use it.
- Template Reference Variables & @ViewChildFix a focus button whose @ViewChild query name doesn’t match the template’s #ref, and a character counter stuck at 0.DrillSolve · EasyDrillSolve · Easy
- ng-template & ngTemplateOutletProFix a reusable list whose caller-supplied row template compiles fine but never actually receives the item it should render.DrillSolve · HardDrillSolve · Hard
- Components & Directives · Dynamic ComponentsHarder component questions where view queries and content projection start to interact.QuizTake · HardQuizTake · Hard
Read next
Two-Way Binding and Reactive Forms
[(ngModel)] is sugar over a property and event bound together; reactive forms trade that convenience for FormControl, validators and explicit state.
2 min
Input, Output and Content Projection
@Input()/@Output() pass data down and events up between components; <ng-content> lets a parent's markup render inside a child it doesn't control.
2 min