如何动态改变绑定的formControlName

How to dynamically change the bound formControlName

我有一个 angular 2 反应形式,有四个 formControl 和一个输入字段。我想要的是要求用户一项一项地填写信息。因此,我将 firstControlName 分配给 ngOnInit 上的 属性 调用 currentFormControlName,并将其与模板文件中的输入字段绑定。当用户填写他的姓名时,该字段将有效,并且在提交时我会将 currentFormControlName 属性 更改为下一个 formControlName。但问题是绑定没有更新。输入字段仍然绑定到 name。当我在输入字段中键入内容时,name 的值正在更新,而不是 email.

app.component.ts

ngOnInit() {
  this.form = this.builder.group({
    'name': ['', Validator.required],
    'email': ['', Validator.email],
    'phone': ['', Validator.required],
    'password': ['', Validator.required],
  });
  this.currentFormControlName = 'name';
}

submit() {
  this.currentFormControlName = 'email'; // Setting it manually just for the demo of this question.
}

app.component.html

<form [formGroup]="form">
  <input type="text" [formControlName]="currentFormControlName">
  <input type="submit" (click)="submit()">
</form>

ngFormNameDirective

你不能那样做,因为 ngFormControlName 指令在这里只使用了一次 @Input() name

@Directive({selector: '[formControlName]'...})
export class FormControlName extends ... {
  @Input('formControlName') name: string;

  ngOnChanges(changes: SimpleChanges) {
    if (!this._added) this._setUpControl(); <------------ here
    if (isPropertyUpdated(changes, this.viewModel)) {

在每个后续更改中 this._added 将是 true。您可以在 ngOnChanges 中看到的下一个检查是 isPropertyUpdated,它不检查 name 输入更改,只检查 model 输入:

export function isPropertyUpdated(changes: {[key: string]: any}, viewModel: any): boolean {
  if (!changes.hasOwnProperty('model')) return false;
  const change = changes['model'];

ngFormDirective

要完成您想要做的事情,您需要使用 ngFormDirective 来检查控件是否已更新:

export class FormControlDirective extends NgControl implements OnChanges {
  viewModel: any;

  @Input('formControl') form: FormControl;

  ngOnChanges(changes: SimpleChanges): void {
    if (this._isControlChanged(changes)) {

  private _isControlChanged(changes: {[key: string]: any}): boolean {
    return changes.hasOwnProperty('form');
  }

但是,这是一个独立的指令。

更新

您还可以使用FormControlDirective在控件之间切换

[formControl]="form.get(currentFormControlName)"

旧答案

假设我们有以下内容

template.html

<form [formGroup]="form" #formDir="ngForm">
    <input type="text" #controlDir [formControlName]="currentFormControlName">
    <input type="submit" (click)="submit()">
</form>
<pre>{{ form.value | json }}</pre>

单击提交按钮后,我们可以更改 currentFormControlName 并使用新名称注册控件,如

component.ts

form: FormGroup;

@ViewChild('formDir') formDir: FormGroupDirective;
@ViewChild('controlDir', { read: FormControlName }) controlDir: FormControlName;

currentFormControlName: string;

constructor(private builder: FormBuilder) {}

ngOnInit() {
    this.form = this.builder.group({
        'name': ['', Validators.required],
        'email': ['', Validators.email],
        'phone': ['', Validators.required],
        'password': ['', Validators.required],
    });
    this.currentFormControlName = 'name';
}

submit() {
    this.formDir.removeControl(this.controlDir);
    this.controlDir.name = this.currentFormControlName = 'email'
    this.formDir.addControl(this.controlDir);
}

之后我们的输入元素将管理 email 值。因此,如果我们在 input 中键入内容,它将反映在 form.email

Plunker Example

此解决方案基于 FormControlName source code

ngOnChanges(changes: SimpleChanges) {
  if (!this._added) this._setUpControl();
  if (isPropertyUpdated(changes, this.viewModel)) {
    this.viewModel = this.model;
    this.formDirective.updateModel(this, this.model);
  }
}

我们可以看到这个指令只注册了一次控制权。但它也有以下 ngOnDestroy 钩子

ngOnDestroy(): void {
  if (this.formDirective) {
    this.formDirective.removeControl(this);
  }
}

这给了我一些想法

Plunker Example