在动态创建某些表单字段的情况下,如何处理反应式表单和字段验证?
How can I handle a reactive form and fields validation in a situation where some form fields are created dynamically?
我正在使用 PrimeNG 开发一个 Angular 项目,我有以下问题需要使用反应形式来解决。
基本上在我的 HTML 中,我有一些东西可以渲染这个:
基本上感兴趣的字段是表示“项目订单 ID”的 Commessa 字段。我想有时候在英语商务语言中会用WSS这个词(Work Breakdown Structure)。
这是我目前使用的代码:
<div class="row">
<div class="col-2">
<p>Commessa</p>
</div>
<div class="col-10">
<input id="commessa" class="p-invalid" aria-describedby="commessa-help" formControlName="commessa" type="text" pInputText required/>
<i class="pi pi-plus-circle"></i>
</div>
</div>
因此,当在我的 TypeScript 组件代码中“提交”表单(这不是真正的提交)时,我有这个 orderForm 对象:
orderForm: FormGroup;
初始化到 ngOnInit() 为我的表单中定义的所有字段设置验证规则:
ngOnInit() {
this.orderFormValues = new OrderFormValues();
console.log("orderFormValues VALUES: " + this.orderFormValues.statoOrdine);
this.orderForm = this.fb.group({
idOrdine: [null, [Validators.required]],
dataInserimentoOrdine: [new Date(), [Validators.required]],
statoOrdine: [null, [Validators.required, Validators.minLength(5)]],
commessa: [null, [Validators.required, Validators.minLength(5)]],
CIG: [null, [Validators.required, Validators.pattern("^[a-zA-Z0-9]{10}$")]],
dataInizioAttivita: [null, [Validators.required]],
dataFineAttivita: [null, [Validators.required]],
referente: [null, [Validators.required]],
ruoloReferente: [null, [Validators.required]],
tipologiaDiPartecipazione: [null, [Validators.required, Validators.minLength(5)]],
quotaPercentualeDiRTI: [null, [Validators.max(100)]],
cliente: [null, [Validators.required]],
vatCliente: [null, [Validators.required]],
clienteFinale: [null, []],
vatClienteFinale: [null, []],
tipologiaContratto: [null, []],
importoContratto: [null, [Validators.required]],
linkContratto: [null, [Validators.required]],
dataSottoscrizioneContratto: [null, [Validators.required]],
nomeSocieta: [null, [Validators.required]],
vatSocieta: [null, []],
buName: [null, [Validators.required]],
presenzaAQ: [false, [Validators.required]],
linkIdentificativoAQ: [null, []],
accordoQuadro: [null, []],
residuoAccordoQuadro: [null, []],
compagineDiAQ: [null, []]
});
到目前为止,我只添加了一个“Commessa”字段(因为用例预见到我的表单所代表的单个订单的单个 ID 订单)。但是现在情况发生了变化,我必须为一个订单处理多个 ID 订单。所以我需要 n 个“Commessa”字段。
其实我最初的想法是添加+图标:
<i class="pi pi-plus-circle"></i>
单击此图标,FE 将添加一个新字段,为新 ID 订单插入第二个、第三个...n 字段。
问题是在这种情况下如何处理我的反应式表单的验证和协调?事实上,目前我必须通过 HTML 中的 formControlName 属性静态定义将与我的 FormGroup 对象相关联的字段.
在具体情况下,我现在有:
<input id="commessa" class="p-invalid" aria-describedby="commessa-help" formControlName="commessa" type="text" pInputText required/>
与以下 FormGroup 元素相关,使用 formControlName="commessa"
commessa: [null, [Validators.required, Validators.minLength(5)]],
这样当用户在前端填写Commessa字段时FormGroup中的commessa元素 被赋值并检查插入的值是否有效。
所以我想添加一个新的输入字段,点击 + 按钮(例如 id="commessa_2" 和 formControlName="commessa_2" 但是如何通过后端处理这种情况?是否可以将带有验证器的新元素添加到我的 FormGroup 对象?
当您不知道表单中字段的确切数量时,处理这种动态情况的好策略是什么? (当某些字段可以像本例那样动态创建时)。
如何聪明地解决这个问题?
Angular 提供 Form Array for this. So instead of creating multiple fields in the Form Group. You can set the commessa field as a Form Array。这允许将字段作为数组动态插入其中。
使用响应式FormArray,我们可以达到要求。
请参考下例
示例代码:
app.component.ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, FormArray, Validators, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private fb: FormBuilder) { }
orderForm = this.fb.group({
idOrdine: [null, [Validators.required]],
commessaList: new FormArray([
new FormControl('', [Validators.required, Validators.minLength(5)])
])
});
get commessaList(): FormArray {
return this.orderForm.get('commessaList') as FormArray;
}
onFormSubmit(): void {
for (let i = 0; i < this.commessaList.length; i++) {
console.log(this.commessaList.at(i).value);
}
}
addCommessaField() {
this.commessaList.push(new FormControl('', [Validators.required, Validators.minLength(5)]));
}
deleteCommessaField(index: number) {
if (this.commessaList.length !== 1) {
this.commessaList.removeAt(index);
}
console.log(this.commessaList.length);
}
}
app.component.html
<div class="container">
<br>
<form [formGroup]="orderForm" (ngSubmit)="onFormSubmit()">
<div class="form-group row">
<label for="idOrdine" class="col-sm-2 col-form-label">Id Ordine</label>
<div class="col-sm-10">
<input type="text" formControlName="idOrdine"
[ngClass]="{'error':orderForm.controls.idOrdine.invalid && orderForm.controls.idOrdine.touched}"
class="form-control" id="idOrdine">
</div>
</div>
<div formArrayName="commessaList">
<div class="form-group row">
<label for="commessa" class="col-sm-2 col-form-label">Commessa</label>
<div class="col-sm-10">
<ng-container *ngFor="let commessa of commessaList.controls; index as idx">
<div class="row">
<div class="col-sm-8">
<input type="text" [ngClass]="{'error':commessa.invalid && commessa.touched}" [formControlName]="idx"
class="form-control" id="commessa">
</div>
<div class="col-sm-2">
<button type="button" *ngIf="idx===0" (click)="addCommessaField()" class="btn btn-success"
[ngClass]="'pad'"><i class="fa fa-plus-circle" aria-hidden="true"></i></button>
<button (click)="deleteCommessaField(idx)" *ngIf="idx!==0" class="btn btn-danger">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
</div>
</ng-container>
</div>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary" [disabled]="orderForm.invalid">Save</button>
</div>
</form>
</div>
app.component.css
.container {
margin: 100px;
}
.error{
background-color: red;
color:#FFF;
}
.row {
display: flex;
align-items: baseline;
justify-content: center;
}
我正在使用 PrimeNG 开发一个 Angular 项目,我有以下问题需要使用反应形式来解决。
基本上在我的 HTML 中,我有一些东西可以渲染这个:
基本上感兴趣的字段是表示“项目订单 ID”的 Commessa 字段。我想有时候在英语商务语言中会用WSS这个词(Work Breakdown Structure)。
这是我目前使用的代码:
<div class="row">
<div class="col-2">
<p>Commessa</p>
</div>
<div class="col-10">
<input id="commessa" class="p-invalid" aria-describedby="commessa-help" formControlName="commessa" type="text" pInputText required/>
<i class="pi pi-plus-circle"></i>
</div>
</div>
因此,当在我的 TypeScript 组件代码中“提交”表单(这不是真正的提交)时,我有这个 orderForm 对象:
orderForm: FormGroup;
初始化到 ngOnInit() 为我的表单中定义的所有字段设置验证规则:
ngOnInit() {
this.orderFormValues = new OrderFormValues();
console.log("orderFormValues VALUES: " + this.orderFormValues.statoOrdine);
this.orderForm = this.fb.group({
idOrdine: [null, [Validators.required]],
dataInserimentoOrdine: [new Date(), [Validators.required]],
statoOrdine: [null, [Validators.required, Validators.minLength(5)]],
commessa: [null, [Validators.required, Validators.minLength(5)]],
CIG: [null, [Validators.required, Validators.pattern("^[a-zA-Z0-9]{10}$")]],
dataInizioAttivita: [null, [Validators.required]],
dataFineAttivita: [null, [Validators.required]],
referente: [null, [Validators.required]],
ruoloReferente: [null, [Validators.required]],
tipologiaDiPartecipazione: [null, [Validators.required, Validators.minLength(5)]],
quotaPercentualeDiRTI: [null, [Validators.max(100)]],
cliente: [null, [Validators.required]],
vatCliente: [null, [Validators.required]],
clienteFinale: [null, []],
vatClienteFinale: [null, []],
tipologiaContratto: [null, []],
importoContratto: [null, [Validators.required]],
linkContratto: [null, [Validators.required]],
dataSottoscrizioneContratto: [null, [Validators.required]],
nomeSocieta: [null, [Validators.required]],
vatSocieta: [null, []],
buName: [null, [Validators.required]],
presenzaAQ: [false, [Validators.required]],
linkIdentificativoAQ: [null, []],
accordoQuadro: [null, []],
residuoAccordoQuadro: [null, []],
compagineDiAQ: [null, []]
});
到目前为止,我只添加了一个“Commessa”字段(因为用例预见到我的表单所代表的单个订单的单个 ID 订单)。但是现在情况发生了变化,我必须为一个订单处理多个 ID 订单。所以我需要 n 个“Commessa”字段。
其实我最初的想法是添加+图标:
<i class="pi pi-plus-circle"></i>
单击此图标,FE 将添加一个新字段,为新 ID 订单插入第二个、第三个...n 字段。
问题是在这种情况下如何处理我的反应式表单的验证和协调?事实上,目前我必须通过 HTML 中的 formControlName 属性静态定义将与我的 FormGroup 对象相关联的字段.
在具体情况下,我现在有:
<input id="commessa" class="p-invalid" aria-describedby="commessa-help" formControlName="commessa" type="text" pInputText required/>
与以下 FormGroup 元素相关,使用 formControlName="commessa"
commessa: [null, [Validators.required, Validators.minLength(5)]],
这样当用户在前端填写Commessa字段时FormGroup中的commessa元素 被赋值并检查插入的值是否有效。
所以我想添加一个新的输入字段,点击 + 按钮(例如 id="commessa_2" 和 formControlName="commessa_2" 但是如何通过后端处理这种情况?是否可以将带有验证器的新元素添加到我的 FormGroup 对象?
当您不知道表单中字段的确切数量时,处理这种动态情况的好策略是什么? (当某些字段可以像本例那样动态创建时)。
如何聪明地解决这个问题?
Angular 提供 Form Array for this. So instead of creating multiple fields in the Form Group. You can set the commessa field as a Form Array。这允许将字段作为数组动态插入其中。
使用响应式FormArray,我们可以达到要求。
请参考下例
app.component.ts
import { Component } from '@angular/core';
import { FormControl, FormGroup, FormArray, Validators, FormBuilder } from '@angular/forms';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
constructor(private fb: FormBuilder) { }
orderForm = this.fb.group({
idOrdine: [null, [Validators.required]],
commessaList: new FormArray([
new FormControl('', [Validators.required, Validators.minLength(5)])
])
});
get commessaList(): FormArray {
return this.orderForm.get('commessaList') as FormArray;
}
onFormSubmit(): void {
for (let i = 0; i < this.commessaList.length; i++) {
console.log(this.commessaList.at(i).value);
}
}
addCommessaField() {
this.commessaList.push(new FormControl('', [Validators.required, Validators.minLength(5)]));
}
deleteCommessaField(index: number) {
if (this.commessaList.length !== 1) {
this.commessaList.removeAt(index);
}
console.log(this.commessaList.length);
}
}
app.component.html
<div class="container">
<br>
<form [formGroup]="orderForm" (ngSubmit)="onFormSubmit()">
<div class="form-group row">
<label for="idOrdine" class="col-sm-2 col-form-label">Id Ordine</label>
<div class="col-sm-10">
<input type="text" formControlName="idOrdine"
[ngClass]="{'error':orderForm.controls.idOrdine.invalid && orderForm.controls.idOrdine.touched}"
class="form-control" id="idOrdine">
</div>
</div>
<div formArrayName="commessaList">
<div class="form-group row">
<label for="commessa" class="col-sm-2 col-form-label">Commessa</label>
<div class="col-sm-10">
<ng-container *ngFor="let commessa of commessaList.controls; index as idx">
<div class="row">
<div class="col-sm-8">
<input type="text" [ngClass]="{'error':commessa.invalid && commessa.touched}" [formControlName]="idx"
class="form-control" id="commessa">
</div>
<div class="col-sm-2">
<button type="button" *ngIf="idx===0" (click)="addCommessaField()" class="btn btn-success"
[ngClass]="'pad'"><i class="fa fa-plus-circle" aria-hidden="true"></i></button>
<button (click)="deleteCommessaField(idx)" *ngIf="idx!==0" class="btn btn-danger">
<i class="fa fa-trash" aria-hidden="true"></i>
</button>
</div>
</div>
</ng-container>
</div>
</div>
</div>
<div>
<button type="submit" class="btn btn-primary" [disabled]="orderForm.invalid">Save</button>
</div>
</form>
</div>
app.component.css
.container {
margin: 100px;
}
.error{
background-color: red;
color:#FFF;
}
.row {
display: flex;
align-items: baseline;
justify-content: center;
}