Reputation: 4044
I'm trying to set focus to any given input after that input changes (or potentially manipulate that same DOM element in other ways). I'm using Reactive Forms and the Form elements are rendered dynamically.
The problem I have is that I can access the element that has been changed using either @ViewChildren(AppTag) ipt!: QueryList<AppTag>
which lets me filter on the element I want, but then I don't know how to grab that element using nativeElement
, or I can use @ViewChildren(AppTag) ipt!: QueryList<ElementRef>
, which (surprisingly) seems to be letting me filter the element in question, but (also surprisingly) I'm still unable to manipulate the DOM element. Either way, I get error: "itm.nativeElement is undefined"
.
To reproduce the error, please enter any value into one of the Inputs
in the following StackBlitz, and then TAB
out of the entry. You'll see the error in the console.
Below are what I think are the relevant pieces of code, but please play around with the full StackBlitz example to see the template and data structure more clearly:
app.component.ts
export class AppComponent implements OnInit {
@ViewChildren(AppTag) ipt !: QueryList<ElementRef>
name = 'Angular';
formCtls: any = {};
form: FormGroup = new FormGroup({});
data: {} = { // raw data
name:
{
first: {values: [""], label: "first name"},
middle: {values: [""], label: "middle name"},
last: {values: [""], label: "last name"}
}
};
dispData: any[] = []; // Data formatted for display/iteration by template
itmNo: number = 0; // Unique ID for each Input
focusItm = 0; // The bridge by which "FormControl.valueChanges" communicates with "QueryList.changes"
. . .
. . .
ngAfterViewInit() {
this.ipt.changes.subscribe(list=>{
setTimeout(()=>
list.filter(itm=>+itm.id===this.focusItm).forEach(itm=>{
console.log(`Item: ${itm.id} Focus: ${this.focusItm} ${+itm.id===this.focusItm}`);
itm.nativeElement.focus(); // <-- HERE'S WHERE I'M HAVING THE TROUBLE
}
),0)}
)
}
. . .
. . .
renderDisplayArray(){
this.dispData = [];
this.itmNo = 0;
. . .
. . .
const i=r;
this.formCtls[ctlName] = new FormControl(itm["values"][r], {updateOn: 'blur'});
this.form.addControl(ctlName,this.formCtls[ctlName]);
const curItm=this.itmNo;
this.formCtls[ctlName].valueChanges.subscribe(val=>{
console.log(`VALUE: ${val}`);
itm["values"][i]=val || '';
this.renderDisplayArray();
this.focusItm = curItm;
})
. . .
. . .
Here's the Directive AppTag
which I apply to the Inputs to be able to filter
and grab the DOM elements I want.
appTag.ts
import { Component, Directive, ElementRef, OnInit, Input } from "@angular/core";
@Directive({
selector: '[appTag]'
})
export class AppTag implements OnInit{
constructor(private el: ElementRef) {}
@Input('appTag') id: number;
}
Upvotes: 1
Views: 4734
Reputation: 106
Just change the visibility from el: ElementRef
in AppTag
to public
@Directive({
selector: '[appTag]'
})
export class AppTag implements OnInit{
constructor(public el: ElementRef) {}
@Input('appTag') id: number;
}
and use in your filter function the el
property of the AppTag
.
Change your problem line to this:
itm.el.nativeElement.focus(); // <-- HERE'S WHERE I'M HAVING THE TROUBLE
Upvotes: 1