1

I try to load some data in ngInit and set width of the div equal to received data. Then I try to set some style options in ngAfterViewInit using ViewChild and ElementRef, but my elementRef is always undefined. How can I fix it?

MyComponent:

export class NewComponent implements OnInit, AfterViewInit {
    public data: number = 0;
    public showMe: bool = false;
    public redColor: bool = false;
    @ViewChild('myElement', { static: false}) myElement;

    constructor(private myService: MyService) {}

    public ngOnInit(): void {
        this.myService.loadData().subscribe(result => this.data = result);
    }

    public ngAfterViewInit(): void {
        console.log(this.myElement); // undefined
        console.log(this.myElement.nativeElement); // Cannot read property 'nativeElement' of undefined
        this.setStyleOptions(); 
    }

    public setStyleOptions() {
        if (this.myElement.nativeElement.clientWidth > 50) {
            this.showMe = true;
            this.redColor = true;
        }
        else {
            this.showMe = false;
            this.redColor = false;
        }
    }
}

My template:

<div *ngIf="some-condition">
    <div *ngIf="some-new-condition">
        <div #myElement [style.width.px]=data"></div>
        <div *ngIf="showMe" [class.red]="redColor">Text</div>
    </div>
</div>

I know that elementRef can be undefined due to *ngIf directive, but I can`t replace it by [hidden], because I will have to replace the directive in all outer div blocks. I also try to use ViewChild setter, like here @ViewChild in *ngIf:

private myElement: ElementRef;
@ViewChild('myElement', { static: false}) set content(content: ElementRef) {
    if (content) {
        this.myElement = content;
        this.setStyleOptions();
    }
}

But then "Еxpression has changed after it was checked" appears in console. Set timeout also doesn`t help.

How can I change template properly?

TryBetter
  • 43
  • 5

1 Answers1

1

when you has a "ViewChild" under a condition, you need "give a change to Angular" to repaint. So, in general you need

condition=true;  //first makes the condition true
setTimeout(()=>{  //say Angular to "repaint" and then execute the function
  this.setStyleOptions()
})

You can also use changeDetectorRef. That's , you inject it in constructor

constructor (private cdr:ChangeDetectorRef){}

And use

condition=true;  //first makes the condition true
this.cdr.markForCheck()     //say Angular to "repaint" 
this.setStyleOptions()  //then execute the function
Eliseo
  • 50,109
  • 4
  • 29
  • 67
  • i shouldn`t touch outer conditions (some-condition, some-new-condition in my sample). Is there any other ways to get elementRef that contains ngIf directive? – TryBetter Mar 05 '21 at 13:05
  • I want to say that if your ViewChild is under a `*ngIf` you need first makes true, repaint and then use the ViewChild, you can not makes true and call to ViewChild because Angular has no time to repaint – Eliseo Mar 05 '21 at 13:15