The Angular @ViewChild decorator
Tags: angular, typescript, javascript, webdev
Published: 2019-06-19 07:39:20

With Angular 8, the static parameter of the @ViewChild decorator [became temporary mandatory](https://angular.io/guide/static-query-migration).
In the previous versions, Angular automatically decided if the query had to be static or dynamic and, as I wasn't familiar with this parameter, I thought it was a good time to dig into it and to write my first blog post ever! :smile:

In this post I'll briefly introduce what's a decorator and how it is used and then we'll dig into the `@ViewChild` decorator and explain the role of its parameters.

## Decorators and the decorator pattern

The word decorator can refer to two different things when talking about programming languages: the decorator pattern, and actual decorators. Let's demystify these concepts!

### The decorator pattern

The decorator pattern is an OOP design pattern allowing to add behavior to a class or a class member dynamically. It means that, for example, we can change the behavior of a class at the instantiation of an object, without changing the behavior of further instantiations. I do not want to dig too deep in the explanation of this design pattern here.

### Decorators

A decorator as we will talk about in this article is a specific implementation of the decorator pattern in a programming language. As this article is about Angular and TypeScript, we will use the word decorator to designate the specific implementation of this design pattern in TypeScript.

Decorators are an experimental TypeScript feature, so breaking changes can be introduced anytime. However, the Angular syntax relies on decorators heavily.

Basically, a decorator in TypeScript is a function that can be attached to a class or a class member (an attribute or a method) using an annotation beginning by `@`. A decorator can take parameters.
For example, we could define a `@isAdmin` decorator used in a component like this:

```typescript
user = this.userService.getUser();

@isAdmin(user)
deleteEntry(entryId: string) {
// Delete some entry.
}
```

And this decorator can be used for example to restrict the access of the method to user who have the admin role.

The decorator declaration could be something like that:

```typescript
import { jwtDecode } from 'jwt-decode';

function isAdmin(user: User) {
  return jwtDecode(user.token).isAdmin;
}
```

Pretty cool, isn't it?
Decorators can help us structure our code by wrapping behavior in reusable functions.

If you are familiar with Angular, you probably noticed how we declare Angular components, modules, etc. For example an Angular component is a class annotated with the `@Component` decorator and this decorator take some parameters like its template URL and its change detection strategy.

Another decorator provided by Angular is `@ViewChild`.It is this one we'll focus is this article!

## The Angular `@ViewChild` decorator

The `@ViewChild` decorator can be applied on a property and allow to configure a **view query**.

### The selector

The first parameter of this decorator is the **selector**. Angular will use the selector to try matching an element in the template, and the property annotated with the decorator will reference the **first matching element**.

A selector can take several forms, so let's explore them and write some examples.

- any class with the `@Component` or `@Directive` decorator

```typescript
@Component({
  selector: 'user-card'
})
export class UserCard {
  @Input() firstName: string;
  @Input() lastName: string;
  @Input() age: number;
}

@Component({
  selector: 'myComp',
  template: `
    <user-card [firstName]="'Roger'" [lastName]="'Dupont'" [age]="53">
    </user-card>
  `
})
export class MyCompComponent {
  @ViewChild(UserCard, { static: false }) userCard: UserCard;
}
```

```typescript
@Directive({
  selector: 'myMenu'
})
export class MenuDirective {
  open() {}
  close() {}
}

@Component({
  selector: 'my-comp',
  template: `
    <div myMenu></div>
  `
})
export class MyCompComponent {
  @ViewChild(MyMenuDirective, { static: false }) menu: MyMenuDirective;
}
```

- a template reference variable as a string

```typescript
@Component({
  selector: 'my-comp',
  template: `
    <div #someElement></div>
  `
})
export class MyCompComponent {
  @ViewChild('someElement', { static: false }) someElement: ElementRef;
}
```

- a TemplateRef

```typescript
@Component({
  selector: 'my-comp',
  template: `
    <ng-template></ng-template>
  `
})
export class MyCompComponent {
  @ViewChild(TemplateRef, { static: false }) someTemplate: TemplateRef;
}
```

_The Angular documentation states there are two other selector possibilities:_

- _any provider defined in the child component tree of the current component (e.g. `@ViewChild(SomeService) someService: SomeService`)_
- _any provider defined through a string token (e.g. `@ViewChild('someToken') someTokenVal: any`)_

_However I have no clue how to apply these cases. If someone has the answer and want to give a hand, she or he would be very welcomed. :wink:_

### The `static` parameter

Here we are, the parameter that became temporary mandatory! Let's see what its role is.

The `static` parameter, and I'm sure you guessed, is here to tell Angular if the query should be ran statically or dynamically. But what does this change in practice?
Basically, it changes **when** the view query will resolve.

Angular recommends retrieving view queries results in the `ngAfterViewInit` [lifecycle hook](https://angular.io/guide/lifecycle-hooks) to ensure that queries matches that are dependent on binding resolutions (like in `*ngFor` loops or `*ngIf` conditions) are ready and will thus be found by the query. To get this behavior, the `static` parameter must be set to `false`.

Let's see an example (open the StackBlitz console to see the logs):

{% stackblitz angular-viewchild-static-false file=src/app/hello.component.ts %}

Setting the `static` parameter to false cover most of our use cases. However, we may encounter situation where we need to access the view query result before the ngAfterVewInit hook is called. Setting `static` to true allow this behavior by allowing to access the view query results in the ngOnInit lifecycle hook, but **it only works for queries taht can be resolved statically**. The element we want to fetch with `@ViewChild` must so not be in a *`ngFor` loop or a `*ngIf` condition.

Let's see an example:

{% stackblitz angular-viewchild-static-true file=src/app/hello.component.ts %}

As said in the Angular documentation, `static` is only mandatory in version 8 to ease the change of default and avoid further errors. By making developers think about this parameter, they are prepared for the next default behavior of `@ViewChild`.

From version 9, the `static` parameter default value will be `false`. The previous behavior (the default value was automatically determined by Angular depending on how the view query result was used) could lead to some tricky bugs.

### The `read` parameter

The `read` parameter is optional. This parameter allows to change the type of the view query result. In fact, each kind of selector has its default type:

- any class with the `@Component` or `@Directive` decorator :arrow_right: the class
- a template reference variable as a string ️️️:arrow_right: `️️️ElementRef`
- a TemplateRef :arrow_right: `TemplateRef`

However, we may want to query using a template reference variable as a string and use the actual type of the targeted element. In the same fashion, we can use a class as a selector and want to access it though the `ElementRef` type.

A non-exhaustive list of examples:

```typescript
@Component({
  selector: 'my-comp',
  template: `
    <user-card #userCard></user-card>
  `
})
export class MyCompComponent {
  // We set read to the UserCard type corresponding to a component class, so the view query result will be of type UserCard.
  @ViewChild('userCard', { read: UserCard, static: false }) userCard: UserCard;
}
```

Using a component or directive class allows to access the properties of this class. For example, a `UserCard` component representing a card with user information could countain a method, and this method could thus be used programatically from the view query result. It would look like `this.userCard.flip();`.

```typescript
@Component({
  selector: 'my-comp',
  template: `
    <user-card></user-card>
  `
})
export class MyCompComponent {
  // We set read to ElementRef so, even if the selector is a component class, the query view result will be of type ElementRef.
  @ViewChild(UserCard, { read: ElementRef, static: false })
  userCard: ElementRef;
}
```

`ElementRef` is a wrapper around a native element, so it is useful to access things like HTML attributes, classes, etc.

```typescript
@Component({
  selector: 'my-comp',
  template: `
    <div #myContainer></div>
  `
})
export class MyCompComponent {
  @ViewChild('myContainer', { read: ViewContainerRef, static: false })
  myList: ViewContainerRef;
}
```

`ViewContainerRef` allows to get the element as container. This is the good choice when we need to manipulate the DOM (for example adding or removing nodes dynamically).

This parameter allows our queries to be very flexible as the returned type can be independent of the kind of selector we choose to use.

## A quick view on `@ViewChildren`

There is another Angular decorator called `@ViewChildren`.

As we saw before, a `@ViewChild` query only return the first matching element. So what if we want to get the list of all matching elements? That's exactly what `@ViewChildren` is for.

It takes a `selector` and a `read` parameter like `@ViewChild`, but no `static`. The only available behavior is dynamic, so the query will only resolve in the `ngAfterViewInit` lifecycle hook.

{% stackblitz angular-viewchildren-always-dynamic file=src/app/hello.component.ts %}

`@ViewChildren` returns a `QueryList` object, which contains an `EventEmitter` object. The `QueryList` is dynamically updated, so if matching elements are added or deleted, the `QueryList` will emit a new event, so we can subscribe on it and react on value change.

## First article in the wild

Yay, you reach the end of my first article ever, congratulations!

Any suggestions and remarks are welcomed 😄

## Useful links and sources

- [TypeScript decorators documentation](https://www.typescriptlang.org/docs/handbook/decorators.html)
- [The decorator pattern Wikipedia page](https://en.wikipedia.org/wiki/Decorator_pattern)
- [Static query migration (why the ViewChild static parameter became temporary mandatory)](https://angular.io/guide/static-query-migration)
- [Angular lifecycle hooks](https://angular.io/guide/lifecycle-hooks)
- [Angular ElementRef documentation](https://angular.io/api/core/ElementRef)
- [Angular TemplateRef documentation](https://angular.io/api/core/TemplateRef)
- [Angular ViewContainerRef documentation](https://angular.io/api/core/ViewContainerRef)