How to dynamically change the binded formControlName

时间:2017-07-12 08:10:55

标签: angular angular-reactive-forms

I have an angular 2 reactive form with four formControls and only one input field. What I want is to ask the user to fill up the infos one by on. So, I'm assigning the firstControlName to a property call currentFormControlName on ngOnInit and binding it with the input field in template file. When the user fill up his name the field will be valid and on submit I'll change the currentFormControlName property to next formControlName. But the problem is the binding isn't updating. The input field is still bonded to name. When I type something on the input field the value of name is updating not 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>

2 个答案:

答案 0 :(得分:2)

假设我们有以下

<强> 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);
}

之后我们的input元素将管理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);
  }
}

给了我一些想法

<强>更新

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

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

<强> Plunker Example

答案 1 :(得分:1)

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');
  }

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