我有一个由 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不是解决方案,因为它仅触发一次,并且如果在初始触发之前连续打开两个或多个字段,则根本无法触发。
答案 0 :(得分:1)
我对自定义字段有疑问。
据我了解,您是从第一个字段提交的。但是事实证明,该字段尚未将其值传递给表单。在这种情况下,我在setTimeout
上做了一点延迟。
答案 1 :(得分:1)
我建议您使用FormBuilder
,FormGroup
和FormArray
。我可以自由地进行一些更改(我从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>