动态表单输入值在首次提交时未合并

时间:2018-07-12 15:04:47

标签: angular forms undefined

我有一个由 n 输入组成的表单:

    <form class="new-user" [formGroup]="customFields">
      <div *ngFor="let customField of customer['customFields']; let i = index">
          <div *ngIf="isEditing(i)" [@phaseAndSlideLeftAnimation] class="cusomer-property">
              <mat-form-field>
                <mat-label i18n="getFormKey(customField)">{{getFormKey(customField)}}</mat-label>
                <input
                  ...
                  (keyup.enter)="finalizeEdit(customField, getFormKey(customField))"
                  (keyup.esc)="cancelEdit(customField)">
                <mat-hint align="end">{{input.value?.length || 0}}/{{maxChars}}</mat-hint>
              </mat-form-field>
          </div>
          <div *ngIf="isNotEditing(i)" [@phaseAndSlideLeftAnimation] class="cusomer-property pointer" (click)="viewEdit(customField)">
              <div>{{getFormKey(customField)}}</div>
              <div>{{getFormValue(customField)}}</div>
        </div>
      </div>
    </form>

这是我的动态表格init:

if(this.customer && this.customer['customFields'].length){
      this.customer['customFields'].forEach((o, i) => {
        if(o['value']){
          this.customFields.addControl(o['name'], new FormControl(o['value'], Validators.required));
        } else {
          this.customer['customFields'][i]['value'] = '';
          this.customFields.addControl(o['name'], new FormControl(o['value'], Validators.required));
        }
      });
    }

我遇到的错误是我单击的第一个字段(然后通过*ngIf切换为mat-forms输入)还没有水合字段。

在我的伪提交函数中尝试:

  finalizeEdit(customField, key){
    console.log(this.customFields['value'][key]);
    customField['value'] = this.customFields['value'][key];
    this.popCustomFields.next(this.customer);
    this.closeEdit(customField); 
  }

this.customFields['value'][key](读取:this.customFields.value.myinput),返回undefined

...

但是,如果我单击并打开第二个“自定义字段”或输入,则所有输入现在都将正确地实例化并以表格的形式正确显示所有值。

出于某种原因,这是针对表单的所有字段的。

您可能认为这是与*ngIf相关的问题之一,但显然不是: 如果我显示一个字段,则显示另一个字段。现在他们两个都准备好了,此后每个领域都准备就绪。

因此,该错误仅在加载视图后的第一个“ input-ngIf-reveal”上发生。

然后我发现了:https://codecraft.tv/courses/angular/forms/submitting-and-resetting/

发现您可以执行此操作:

<form (ngSubmit)="onSubmit()">
  .
  .
  .
</form>

希望是,即使在首次提交时,此功能在被触发时也能正确接收到水合值...

因为就我而言,我不在乎提交是否在整个表单上被触发,所以这可能对我有用。

这是我的难题:

要使用此功能,假设我想将customField作为参数传递给finalizeEdit(),则需要在DOM中放置<form>标签的开头和结尾,在*ngFor中,以便能够将提交所需的变量传递给它。

问题是这将导致 n 形式为1个输入,而不是1个 n 形式,破坏表单后面的整个代码。

我觉得那会使我的代码更糟。

另外,我不得不求助于将每个表单提取到组件中以正确初始化表单。

所有这些都无法保证这将解决我的“首次点击”错误。

理想情况下,我可以将(ngSubmit)="onSubmit()"放在<input>上,而不是放在<form>上,但是我尝试过,但这不是可用的方法。

在任何情况下,我都怀疑表格的onSubmit不能解决我的不适。为什么我的输入字段在第一次尝试时没有实例化?

编辑(16/07/2018):

试用后,我可以确认onSubmit不是解决方案,因为它仅触发一次,并且如果在初始触发之前连续打开两个或多个字段,则根本无法触发。

2 个答案:

答案 0 :(得分:1)

我对自定义字段有疑问。 据我了解,您是从第一个字段提交的。但是事实证明,该字段尚未将其值传递给表单。在这种情况下,我在setTimeout上做了一点延迟。

答案 1 :(得分:1)

我建议您使用FormBuilderFormGroupFormArray。我可以自由地进行一些更改(我从Material包中省略了组件,并更改了customers数据的结构以简化工作),并实现了我认为可以解决您的问题的方法。

这是StackBlitz上的working example

模块

import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule, ReactiveFormsModule } from '@angular/forms';

import { AppComponent } from './app.component';

@NgModule({
  imports: [BrowserModule, FormsModule, ReactiveFormsModule],
  declarations: [AppComponent],
  bootstrap: [AppComponent]
})
export class AppModule { }

组件

import { Component, OnInit } from '@angular/core';
import { FormBuilder, FormGroup, FormArray, FormControl, Validators } from '@angular/forms';
import { distinctUntilChanged, debounceTime } from 'rxjs/operators';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent implements OnInit {

  form: FormGroup;
  customer = {
    customFields: [
      { value: 'one', key: 'first', isEditing: false },
      { value: 'two', key: 'second', isEditing: false },
      { value: 'three', key: 'third', isEditing: false },
      { value: 'four', key: 'fourth', isEditing: false }
    ]
  };

  get fields() {
    return this.form.get('fields') as FormArray;
  }

  constructor(private fb: FormBuilder) { }

  ngOnInit() {
    this.form = this.fb.group({
      fields: this.fb.array([])
    });
    if (this.customer && this.customer.customFields && this.customer.customFields.length) {
      this.customer.customFields.forEach(field => {
        if (!field.value) {
          field.value = '';
        }
        this.fields.push(this.fb.control(field.value, Validators.required));
      });
    }
    this.watchFormChanges();
  }

  saveEdit(index: number) {
    this.customer.customFields[index].value = this.fields.at(index).value;
    this.toggleEdit(index);
  }

  cancelEdit(index: number) {
    this.fields.at(index).setValue(this.customer.customFields[index].value);
    this.toggleEdit(index);
  }

  toggleEdit(index: number) {
    const isEditing = this.customer.customFields[index].isEditing;
    this.customer.customFields[index].isEditing = !this.customer.customFields[index].isEditing;
  }

  private watchFormChanges() {
    this.form.get('fields').valueChanges
      .pipe(distinctUntilChanged(), debounceTime(500))
      .subscribe(value => console.log(value));
  }

}

HTML

<form [formGroup]="form">
  <div formArrayName="fields">
    <div *ngFor="let field of fields.controls; let i=index">

      <ng-container *ngIf="customer.customFields[i].isEditing; then editing else displaying"></ng-container>

      <ng-template #editing>
        <div class="field">
          <label [for]="i">{{ customer?.customFields[i].key }}:</label>
          <input [id]="i" class="value" type="text" [formControlName]="i (keyup.enter)="saveEdit(i)" (keyup.esc)="cancelEdit(i)">
        </div>
      </ng-template>

      <ng-template #displaying>
        <div class="field">
          <label>{{ customer?.customFields[i].key }}:</label>
          <span class="value" [title]="fields.getRawValue()[i]" (click)="toggleEdit(i)">{{fields.getRawValue()[i]}}</span>
        </div>
      </ng-template>

    </div>
  </div>
</form>