2

I am trying to select some text in an input field after a component is initialised. I know I can do this with a setTimeout(), but that feels a bit too hacky.

Most hooks run before the input text is loaded by two-way binding. The others also run every time someone selects the other field.

The code: https://stackblitz.com/edit/angular-pxrssq

import { Component, OnInit, Input, ViewChild } from '@angular/core';

@Component({
  selector: 'app-child',
  template: `
    <input #input type="text" [(ngModel)]="info.one"/>
    <input type="text" [(ngModel)]="info.two"/>`,
  styleUrls: ['./child.component.css']
})
export class ChildComponent implements OnInit {
  @Input() info;
  @ViewChild('input', {static: true} ) input;

  constructor() { }

  ngOnInit() {
    this.input.nativeElement.select();
  }

}

Is there a lifecycle hook that runs once after a components initialisation, but after two-way-binding is loaded?

HDJEMAI
  • 9,436
  • 46
  • 67
  • 93
Rob Monhemius
  • 4,822
  • 2
  • 17
  • 49
  • @SiddAjmera Nope, I tried `AfterViewInit`. It should select the text, it doesn't ( so that loads before the two-way binding is completed ). https://stackblitz.com/edit/angular-hsqvrq – Rob Monhemius Jun 21 '19 at 23:01

1 Answers1

4

as a matter of fact, using setTimeout() is not a hacky solution it is the solution. because of angular's unidirectional data flow rule.

Angular's unidirectional data flow rule forbids updates to the view after it has been composed.

in order to observe consequences of this rule, try assigning a new value to info property of ChildComponent on AfterViewInit hook. you'll get an ExpressionChangedAfterItHasBeenCheckedError which is angular's way of saying that "updates to the view after it has been composed is forbidden!"

since you are not updating any of your component's data-bound properties and just making dom manipulation, angular is not throwing any error but it is not reflecting your changes to dom as well.

coming back to your question;

Is there a lifecycle hook that runs once after a components initialisation, but after two-way-binding is loaded?

yes there is and it is AfterViewInit hook. Because;

View queries are set before the ngAfterViewInit callback is called.

and DOM updates are handled before ngAfterViewInit is called as explained in here.

and it comes after OnChanges hook, where;

OnChanges is called when any data-bound property of a directive changes.

consequently you should do as follows if you want the text get selected just once when the first time component is loaded.

ngAfterViewInit() {
  setTimeout(() => this.input.nativeElement.select());
}

if you want to select text whenever input property gets changed by parent component (including first load) then you need to implement OnChanges hook.

ngOnChanges() {
  setTimeout(() => this.input.nativeElement.select());
}

here is a working demo https://stackblitz.com/edit/angular-owahps

hope it helps.

ysf
  • 4,634
  • 3
  • 27
  • 29