[Solved] Angular @ViewChild() error: Expected 2 arguments, but got 1

Article Mohaimen Khalid

Problem:

When trying ViewChild I am getting the error. Error is "An argument for 'opts' was not provided."

Shows error: ts(11,2): error TS2554: Expected 2 arguments, but got 1.

1st know about @ViewChild() decorator

The Angular @ViewChild decorator is one of the first decorators that you will run into while learning Angular, as it's also one of the most commonly used decorators.

This decorator has a lot of features: some of them might not be very well known but they are extremely useful.

When to use the @ViewChild decorator?

Many times we can coordinate these multiple components and HTML elements directly in the template by using template references without using the AppComponent class.

But this is not always the case! Sometimes, the AppComponent might need references to the multiple elements that it contains inside its template, in order to interaction.

If that's the case, then we can obtain references to those template elements and have them injected into the AppComponent class by querying the template: that's what @ViewChild is for.


If that's the case, then we can obtain references to those template elements and have them injected into the AppComponent class by querying the template: that's what @ViewChild is for.

To understand this, suppose we have two components, a child and a parent one. Since the child component can be located inside the parent component, it can accessed as @ViewChild.

//AppComponent
    export class AppComponent implements OnInit, AfterViewInit {
        message: any;
        @ViewChild(ChildComponent) chviewChild: ChildComponent;

        ngAfterViewInit() {
            console.log(this.chviewChild);
        }

        ngOnInit() {
            this.message = 'Hello World !';
        }
    }

In the above code, we imported @ViewChild decorator and then imported the lifecycle hook AfterViewInit and implemented it. Now, to change the property value, we can make use of the viewchild we just create like

ngAfterViewInit(){
        this.chviewChild.message = ‘Changed value of View Child’
    }

This will output the value Changed value of View Child, but will throw an error saying “Expression has changed after it was last checked”.

To handle this error, we use Change detection using ChangeDetectorRef

constructor(private cd: ChangeDetectorRef) {}
    
    ngAfterViewInit() {
        console.log(this.chviewChild);
        this.chviewChild.message = 'Changed value of View Child';
        this.cd.detectChanges();
    }

Now, we can update the value of the message property using Viewchildren inside the ngAfterViewinit() life cycle hook like this:

this.chviewChildren.forEach((item) => {item.message = ‘Updated Value’;});
Let us look at the final code now:

import { Component, Input, OnInit, ViewChild, AfterViewInit, ChangeDetectorRef, ViewChildren } from "@angular/core";
    import { ChildComponent } from "./child.component";
    import { QueryList } from "@angular/core";

    @Component({
      selector: 'app-root',
      template:`
      <app-child *ngFor = 'let i of message' [message] = 'i'></app-child>
      `
    })
    export class AppComponent implements OnInit, AfterViewInit{
        constructor(private cd: ChangeDetectorRef){}
    ngAfterViewInit() {
        console.log(this.chviewChildren);
        this.chviewChildren.forEach((item) =>{item.message = "Updated Value"; });
        this.cd.detectChanges();
    }
    message: any;
    @ViewChildren(ChildComponent) chviewChildren: QueryList<ChildComponent>;

    ngOnInit(){
        this.message = this.getMessage();
    }
    getMessage(){
        return[
            'Hi',
            'Hello',
            'How are you?
        ];
    }
    }
Solution of this question -

In Angular 8 , ViewChild takes 2 parameters

@ViewChild(ChildDirective, {static: false}) Component