ContentChild, ViewChild, template reference variables
In Angular, it is customary to write declarative code. This means that we should not manually request the entities we need. The framework has tools for working with template elements that will help us. We’ll talk about them today.

Who is who
To begin with, let’s figure out what is view and what is content.
view is the template of our component, the directives do not have it.
Content is what our component or directive wraps.
Components will need to add a tag ng-content
into the template, otherwise all content will be replaced with the component’s template when rendering.
Performance Tip: even if ng-content is hidden behind *ngIf
and is not attached to the document, it still renders and goes through all the change checking cycles. If you need lazy initialization – use ng-template
.
ViewChild
When we create a component, we may need to access parts of its template. It can be obtained through the decorator @ViewChild
. First you need to provide a selector – you can mark an element with a string:
<div #ref>...</div>
And then request it through a decorator: @ViewChild(‘ref’)
.
#ref
called template reference variableand we will discuss them in detail below.
You can also use a class if you need to get a component or directive: @ViewChild(MyComponent)
. It can be a DI token: @ViewChild(MY_TOKEN)
.
Requests through decorators are processed within lifecycle hooks, ngOnInit
, ngAfterViewInit
and others. More about them in the next article.
The decorator’s second argument, the options object, is especially useful. The first key is simple – static: boolean
. It tells Angular that an element always exists, not by condition:
static: true
means that this element will be available inngOnInit
;static: false
means the element will only appear inngAfterViewInit
when the whole pattern is validated.
The default value is false
which means that the result will be obtained only after passing the first change check.
<div #static>
Я статичный ребёнок
</div>
<div *ngIf="true" #dynamic>
Я не статичный ребёнок
</div
I note that even static queries give results only in ngOnInit
so technically some time the value is undefined
. It’s good practice to mark this in types so you don’t accidentally refer to them in the constructor.
The second key is read
. The first decorator argument tells Angular to “find me an injector that has this entity” and read
says what to take from this injector. If we do not write it, then we will get the token itself from the first argument.
You can request any entity from the target injector: service, token, component, and so on. The most common use of this is to get the DOM element of the target component:
@ViewChild(MyComponent, { read: ElementRef })
readonly elementRef?: ElementRef<HTMLElement>
Template Reference Variables
Often in @ViewChild
no need at all – in many cases it is perfect template reference variable and passing it to the event handler:
<input #input type="text" [(ngModel)]="model">
<button (click)="onClick(input)">Focus input</button>"
// Обратите внимание, что тут HTMLElement, а не ElementRef
onClick(element: HTMLElement) {
element.focus();
}
You can consider the template reference variable as a kind of closure in the template – the reference is there, but it is available only where needed. This keeps the component code clean.
The Template reference variable refers to the component’s instance if placed on it, or to the DOM element if the component is not there. You can also get the essence of the directive, for this you use exportAs
in decorator @Directive
:
@Directive({
selector: '[myDirective]',
exportAs: 'someName',
})
export class MyDirective {}
<div myDirective #ref="someName">...</div>
ViewChildren
Sometimes you need to get many elements of the same type. In such a situation, you can use @ViewChildren
– mark many elements in the template with the same line and get the entire collection.
All of the above applies here as well. static
is not available for lists and the field type will be QueryList<T>
. This allows you to loop through each element as needed. Consider an example of a tab component: instead of a horizontal scroll, we want to hide extra tabs in the “More” item. IN stackblitz below @ViewChildren
used for implementation like this:
Content
Content can be a convenient way to customize components. In Angular, it resembles slots from native web components and allows you to project content to different parts of the template using the tag ng-content
.
ng-content
allows you to flexibly scatter parts of content to different places using an attribute select
. Its syntax is similar to selector
in directives and components – you can bind to tag names, classes, attributes and combine it in every possible way, up to negation through :not()
. You can always leave ng-content
without a selector so that all other content falls into it.
It’s important to remember that while the content is DOM-bound within a component’s view, it’s actually part of the parent view and follows its change-check cycles. This means that if an element in the content is marked for validation, for example, through the occurrence of an event from @HostListener
, then the view of the content-wrapping component will not be checked. So if a component depends on content, make sure you don’t miss changes to it.
changes: Observable<void>
from QueryList
will help keep track of changes in the lists in the content.
ContentChild and ContentChildren
Content can be accessed in the same way as a component template. Angular has similar decorators @ContentChild
And @ContentChildren
with the same syntax.
Here is an example of a menu component where items are passed as content. This allows the developer to hook into events such as clicks or key presses without messing with the menu logic. The component itself is responsible for keyboard navigation.
Some of the syntax for content decorators differs from view. They have an additional option parameter descendants: boolean
. It allows you to get children from content that is in the content of another nested component, but not in their view:
<!-- @ContentChild('child', { descendants: true }) -->
<my-component>
<another-component>
<div #child>...</div>
</another-component>
</my-component>
With such features in Angular, you can achieve a lot. Practice in working with views and content will allow you to notice where this knowledge will help you create reliable, easily maintainable components!