如何以编程方式将焦点设置为Angular2中动态创建的FormControl

use*_*686 18 forms angular

我似乎无法在动态添加的FormGroup中将焦点设置在输入字段上:

addNewRow(){
    (<FormArray>this.modalForm.get('group1')).push(this.makeNewRow());
    // here I would like to set a focus to the first input field
    // say, it is named 'textField'

    // but <FormControl> nor [<AbstractControl>][1] dont seem to provide 
    // either a method to set focus or to access the native element
    // to act upon
}
Run Code Online (Sandbox Code Playgroud)

如何将焦点设置为angular2 FormControl或AbstractControl?

gon*_*ish 20

您不能设置为FormControlAbstractControl,因为它们不是DOM元素.您需要做的是以某种方式对它们进行元素引用,并调用.focus()它.您可以通过ViewChildren(目前不存在API文档,2016-12-16)实现此目的.

在您的组件类中:

import { ElementRef, ViewChildren } from '@angular/core';

// ...imports and such

class MyComponent {
    // other variables
    @ViewChildren('formRow') rows: ElementRef;

    // ...other code
    addNewRow() {
        // other stuff for adding a row
        this.rows.first().nativeElement.focus();
    }
}
Run Code Online (Sandbox Code Playgroud)

如果你想专注于最后一个孩子......this.rows.last().nativeElement.focus()

在你的模板中,例如:

<div #formRow *ngFor="let row in rows">
    <!-- form row stuff -->
</div>
Run Code Online (Sandbox Code Playgroud)

编辑:

我实际上发现了一个CodePen,有人在做你正在寻找的东西https://codepen.io/souldreamer/pen/QydMNG

  • 我认为它是`@ViewChildren('formRow')行:QueryList <ElementRef>;` (5认同)
  • 按照建议访问它是对 DOM 的直接访问,因此存在“安全风险”。只是将此信息附加到接受的答案中:您现在应该使用 Renderer2 `this.renderer.selectRootElement('#input').focus();` (3认同)

Ken*_*Ken 8

对于Angular 5,结合以上所有答案如下:

组件相关代码:

 import { AfterViewInit, QueryList, ViewChildren, OnDestroy } from '@angular/core';
 import { Subscription } from 'rxjs/Subscription';

 // .. other imports

 export class MyComp implements AfterViewInit, OnDestroy {
   @ViewChildren('input') rows: QueryList<any>;
   private sub1:Subscription = new Subscription();
   //other variables ..

 // changes to rows only happen after this lifecycle event so you need
 // to subscribe to the changes made in rows.  
 // This subscription is to avoid memory leaks
 ngAfterViewInit() {
   this.sub1 = this.rows.changes.subscribe(resp => {
     if (this.rows.length > 1){
       this.rows.last.nativeElement.focus();
     }
   });
  }

  //memory leak avoidance
  ngOnDestroy(){
    this.sub1.unsubscribe();
  }


   //add a new input to the page
   addInput() {
     const formArray = this.form.get('inputs') as FormArray;

     formArray.push(
        new FormGroup(
        {input: new FormControl(null, [Validators.required])}
     ));
    return true;
   }

 // need for dynamic adds of elements to re 
 //focus may not be needed by others
 trackByFn(index:any, item:any){
    return index;
 }
Run Code Online (Sandbox Code Playgroud)

模板逻辑看起来像这样:

 <div formArrayName="inputs" class="col-md-6 col-12" 
     *ngFor="let inputCtrl of form.get('phones').controls; 
             let i=index; trackBy:trackByFn">
     <div [formGroupName]="i">
        <input #input type="text" class="phone" 
             (blur)="addRecord()"
             formControlName="input" />
     </div>
 </div>
Run Code Online (Sandbox Code Playgroud)

在我的模板中,我添加了一个模糊记录,但您可以轻松设置一个按钮来动态添加下一个输入字段.重要的是,使用此代码,新元素可以根据需要获得焦点.

让我知道你的想法


Omk*_*dav 6

这是 Angular 推荐的安全方法

@Component({
  selector: 'my-comp',
  template: `
    <input #myInput type="text" />
    <div> Some other content </div>
  `
})
export class MyComp implements AfterViewInit {
  @ViewChild('myInput') input: ElementRef;

  constructor(private renderer: Renderer) {}

  ngAfterViewInit() {
    this.renderer.invokeElementMethod(this.input.nativeElement,    
    'focus');
  }
}
Run Code Online (Sandbox Code Playgroud)

  • 你也可以 `input.nativeElement.focus();` 但这是对 DOM 的直接访问。因此存在安全风险。或者使用 `Renderer2` `this.renderer.selectRootElement('#input').focus();` 为了完整性。 (10认同)
  • 真的。不过,顺便说一句:Renderer 已被弃用,现在现有的 Renderer2 上没有“invokeElementMethod”方法。 (3认同)