For example, my template looks like -
<div #parent>
<button (click)="showInput()"> Show </button>
<input #inp *ngIf="showInpField" />
</div>
And this is how my compoent looks like:
import { Component, OnInit, ViewChild } from '@angular/core';
@Component({
selector: 'some-component',
templateUrl: './template.html',
})
export class SomeComponent {
@ViewChild('inp') inpField: any;
showInpField = false;
constructor() {}
showInput(){
this.showInpField = true;
// This doesn't work
this.qaTitle.inpField.focus();
}
}
In this case the focus doesn't work because the input element is still yet to be render. I realize that using timeout on that line will work, but somehow I don't feel like it's a good way to do it.
I guess somehow I have detect the change inside the parent div, and on that event I should do the focus operation. How would I do that? I feel like QueryList can be helpful but I can't figure out how do I use it in this case!
@Input () is basically a decorator to bind a property as an input. It is used to pass data i.e property binding from one component to other or we can say, from parent to child component. It is bound with the DOM element. When the DOM element value is changed, Angular automatically updates this property with the changed value.
The parent component was able to set the value of the child DOM Element. ViewChild makes it possible to access a child component and call methods or access instance variables that are available to the child. Let’s say we have a ChildComponent. Ideally, you will use @angular/cli to generate your component:
The Child can send data to Parent by raising an event, Parent can interact with the child via local variable or Parent can call @ViewChild on the child. We will look at all those options in this article. Applies to: Angular 2 to the latest edition of i.e. Angular 8. Angular 9, Angular 10, Angular 11, Angular 12
So we can consider angular component as a way to create custom element and directive as a custom attribute. (for now) An attribute/tag become a child of another if that is used inside the other tag's component html. Child can pass some data to parent via event binding.
This could work
@Component({
selector: 'my-app',
template: `
<div>
<button (click)="toggleInput()"> {{ showInpField ? 'Hide' : 'Show'}} </button>
<input #inp *ngIf="showInpField" />
</div>
`
})
export class AppComponent {
@ViewChildren('inp', { read: ElementRef }) elemRefs: QueryList<ElementRef>;
showInpField = false;
constructor(private renderer: Renderer){}
toggleInput(){
this.showInpField = !this.showInpField;
}
ngAfterViewInit() {
this.elemRefs.changes.subscribe(item => {
if(!this.elemRefs.length) return;
this.renderer.invokeElementMethod(this.elemRefs.first.nativeElement, 'focus');
})
}
}
Plunker Example
Official way would be to use the Renderer
!
https://angular.io/docs/ts/latest/api/core/index/Renderer-class.html
import {Component, NgModule, Renderer, ViewChild} from '@angular/core'
import {BrowserModule} from '@angular/platform-browser'
@Component({
selector: 'my-app',
template: `
<div>
<h2 (click)="toggleInput()">Hello {{name}}</h2>
<input value="any other input.." />
<br />
<input #inpt *ngIf="showInput" />
</div>
`,
})
export class App {
@ViewChild('inpt') input;
name:string;
showInput = false;
constructor(private _renderer: Renderer) {
this.name = 'Angular2'
}
private toggleInput() {
this.showInput = !this.showInput;
if (this.showInput) {
setTimeout(() => {
this._renderer.invokeElementMethod(this.input.nativeElement, 'focus');
}, 1);
}
}
}
@NgModule({
imports: [ BrowserModule ],
declarations: [ App ],
bootstrap: [ App ]
})
export class AppModule {}
live demo: https://plnkr.co/edit/DWaEvqjFARRsKg1Byeu7?p=preview
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With