用逻辑复制表格
Duplicating forms with logic
我创建了一个动态表单字段,我通过按钮上的回调函数添加和删除该字段 DelBtn()
以删除项目和 AddBtn()
以添加项目
每个表单字段都有一个 value
输入
我还有一个 totalval
字段。我希望所有 value
字段中的值总和不超过 totalval
。如果是,我们会显示一条错误消息,如果值等于,我们会显示 reason
表单字段。
示例:
如果我有 totalValue = 100
。现在假设我有我的第一个表单字段 value = 90
.
然后我复制表格并在下一个字段中设置 value = 10
然后应该出现 reason
字段,因为 90 + 10 = 100
。
由于已达到 totalValue
,因此应显示 reason
表单字段并且应禁用添加按钮。
如果在第二次尝试中,如果用户尝试输入大于 10 的值,则应显示一条错误消息。
下面是我当前的代码
在我的 TS 文件中我有
ischecks: boolean = true;
formsArray = [""];
count: number = 0;
totalval: number = 100;
ngOnInit(): void {}
constructor() {}
clickCount(): void {
this.count++;
}
DelBtn = delIndex => this.formsArray.splice(delIndex, 1);
AddBtn = () => this.formsArray.push("");
HTML
<h2> Form</h2>
<pre style="font-weight: bolder;font-family:verdana;margin-left: 35px;">Total Value:{{totalval}} </pre>
<div *ngFor="let i of formsArray; let a = index">
<div>
<form>
<table>
<div>
<label for="fname">Value:</label><br>
<input type="text" id="fname" name="fname" ><br>
<tr>
<td>
<button type="button" class="btn btn-outline-success"
style="border-radius:40px;margin-left: 50px" (click)="DelBtn(a)" ><span class="fa fa-plus"></span>Delete</button>
</td>
</tr>
<tr>
</div>
</table>
</form>
</div>
</div>
<div *ngIf=ischecks style="margin-left:35%">
<label for="fname">Reason:</label><br>
<input type="text" id="fname" name="fname" ><br>
</div>
<br>
<button type="button" class="btn btn-outline-success"
style="border-radius:40px;margin-left: 50px;margin-bottom: 30%;" (click)="AddBtn()" ><span class="fa fa-plus"></span>Add</button>
https://stackblitz.com/edit/angular-ivy-3pbdwv?file=src%2Fapp%2Fapp.component.html
注意:如果你不明白这个问题,请随时在评论中问我,我正在 angular(typescript)
基本 Javascript
很难做到这一点。下面的方法使用 ReactiveForm
方法
我们按照以下步骤进行
- 将
ReactiveFormsModule
添加到模块的 imports
数组中
@NgModule({
imports:[ ReactiveFormsModule, ... ],
- 注入
FormBuilder
class
constructor(private fb: FormBuilder) {}
- 定义表单
myForm = this.fb.group({
totalVal: [100],
formsArray: this.fb.array([this.fb.control("", { validators: [Validators.required] })]),
reason: ["", [Validators.required]]
}, { validators: [sumMatches] });
我们添加了一个自定义验证器 sumMatches
。我们将使用它来检查总值的总和是否已匹配
function sumMatches(control): ValidationErrors | undefined {
const totalVal = Number(control.get("totalVal").value);
const formsArrayTotal = control
.get("formsArray")
.value.reduce((a, b) => Number(a) + Number(b), 0);
if (formsArrayTotal !== totalVal) {
return {
sumMismatch: true
};
}
return;
}
- 接下来我们定义辅助
getter
函数以从 formGroup
中提取属性
get sumMismatch(): boolean {
return this.myForm.hasError('sumMismatch')
}
get arrayFullyFilled() {
return !this.formsArray.controls.some(item => item.errors)
}
get formsArray() {
return this.myForm.get("formsArray") as FormArray;
}
get totalVal() {
return this.myForm.get("totalVal") as FormControl;
}
- 我们还需要修改函数以在 formArray 中添加和删除项目
DelBtn = delIndex => this.formsArray.controls.splice(delIndex, 1);
AddBtn = () => this.formsArray.push(this.fb.control(""));
- 终于可以在html
中实现formGroup
了
<h2> Form</h2>
<span class='totalVal'>Total Value:{{ totalVal.value }}</span>
<form [formGroup]='myForm'>
<ng-container formArrayName='formsArray'>
<table *ngFor="let item of formsArray.controls; let i = index">
<tr>
<td>
<div>
<label [attr.for]="'fname' + i">Value:</label><br>
<input type="number" [formControlName]="i" type="text" [id]="'fname' + i" name="fname" ><br>
</div>
</td>
<td>
<button type="button" class="btn btn-outline-success"
s (click)="DelBtn(i)" ><span class="fa fa-plus"></span>Delete</button></td>
<tr>
</table>
</ng-container>
<div *ngIf='!sumMismatch && arrayFullyFilled'>
<label for="fname">Reason:</label><br>
<input type="text" id="fname" name="fname" ><br>
</div>
<br>
<button type="button" class="btn btn-outline-success"
(click)="AddBtn()" ><span class="fa fa-plus"></span>Add</button>
<br>
<span class="error" *ngIf="sumMismatch && myForm.touched">Total Value Mismatch</span>
</form>
我已经将 css 提取到自己的文件中
.totalVal {
font-weight: bolder;
font-family: verdana;
}
.btn-outline-success {
border-radius: 40px;
margin-left: 50px;
}
.error {
color: red;
}
编辑 1 - 验证器如何工作?
为了理解这一点,我们来看看我们是如何构建表单组的。我们定义了一个结构,它以
的形式产生一个值
{
totalVal: 100,
formsArray: [''],
reason: ''
}
通过将我们的表单组定义为 this.fb.group({ ... }, {validators: [ sumMatches ] }
具有上述值的表单组将被传递给 sumMatches 函数
在 sumMatches
中,我们将有一个类似 formGroup
的值
{
totalVal: 100,
formsArray: ['50', '20', '10'],
reason: ''
}
在上面,我们使用与formArray
相同的control.get('totalVal').value
从formGroup
中提取100。由于 formArray
值将是一个数组,因此我们可以使用 reduce
函数对其求和。我们最终将其与 return null 进行比较(如果它们匹配)和 Object
(如果匹配)不匹配。
使用上述方法,angular 响应式表单将根据用户提供的内容更新表单 valid
状态的值。因此,我们可以利用此 valid
状态来更新 UI
arrayFullyFilled()
get arrayFullyFilled() {
return !this.formsArray.controls.some(item => item.errors)
}
上面的代码试图找出用户是否已经填充了ALL数组中的输入。在我们的数组中,我们获取所有控件,检查其中是否有错误,如果有错误 return false 否则 return true。考虑到在我的 formGroup
中,我使用 Validators.required
验证
将 formControls
设为 required
,这是可能的
我创建了一个动态表单字段,我通过按钮上的回调函数添加和删除该字段 DelBtn()
以删除项目和 AddBtn()
以添加项目
每个表单字段都有一个 value
输入
我还有一个 totalval
字段。我希望所有 value
字段中的值总和不超过 totalval
。如果是,我们会显示一条错误消息,如果值等于,我们会显示 reason
表单字段。
示例:
如果我有 totalValue = 100
。现在假设我有我的第一个表单字段 value = 90
.
然后我复制表格并在下一个字段中设置 value = 10
然后应该出现 reason
字段,因为 90 + 10 = 100
。
由于已达到 totalValue
,因此应显示 reason
表单字段并且应禁用添加按钮。
如果在第二次尝试中,如果用户尝试输入大于 10 的值,则应显示一条错误消息。
下面是我当前的代码
在我的 TS 文件中我有
ischecks: boolean = true;
formsArray = [""];
count: number = 0;
totalval: number = 100;
ngOnInit(): void {}
constructor() {}
clickCount(): void {
this.count++;
}
DelBtn = delIndex => this.formsArray.splice(delIndex, 1);
AddBtn = () => this.formsArray.push("");
HTML
<h2> Form</h2>
<pre style="font-weight: bolder;font-family:verdana;margin-left: 35px;">Total Value:{{totalval}} </pre>
<div *ngFor="let i of formsArray; let a = index">
<div>
<form>
<table>
<div>
<label for="fname">Value:</label><br>
<input type="text" id="fname" name="fname" ><br>
<tr>
<td>
<button type="button" class="btn btn-outline-success"
style="border-radius:40px;margin-left: 50px" (click)="DelBtn(a)" ><span class="fa fa-plus"></span>Delete</button>
</td>
</tr>
<tr>
</div>
</table>
</form>
</div>
</div>
<div *ngIf=ischecks style="margin-left:35%">
<label for="fname">Reason:</label><br>
<input type="text" id="fname" name="fname" ><br>
</div>
<br>
<button type="button" class="btn btn-outline-success"
style="border-radius:40px;margin-left: 50px;margin-bottom: 30%;" (click)="AddBtn()" ><span class="fa fa-plus"></span>Add</button>
https://stackblitz.com/edit/angular-ivy-3pbdwv?file=src%2Fapp%2Fapp.component.html
注意:如果你不明白这个问题,请随时在评论中问我,我正在 angular(typescript)
基本 Javascript
很难做到这一点。下面的方法使用 ReactiveForm
方法
我们按照以下步骤进行
- 将
ReactiveFormsModule
添加到模块的imports
数组中
@NgModule({
imports:[ ReactiveFormsModule, ... ],
- 注入
FormBuilder
class
constructor(private fb: FormBuilder) {}
- 定义表单
myForm = this.fb.group({
totalVal: [100],
formsArray: this.fb.array([this.fb.control("", { validators: [Validators.required] })]),
reason: ["", [Validators.required]]
}, { validators: [sumMatches] });
我们添加了一个自定义验证器 sumMatches
。我们将使用它来检查总值的总和是否已匹配
function sumMatches(control): ValidationErrors | undefined {
const totalVal = Number(control.get("totalVal").value);
const formsArrayTotal = control
.get("formsArray")
.value.reduce((a, b) => Number(a) + Number(b), 0);
if (formsArrayTotal !== totalVal) {
return {
sumMismatch: true
};
}
return;
}
- 接下来我们定义辅助
getter
函数以从formGroup
中提取属性
get sumMismatch(): boolean {
return this.myForm.hasError('sumMismatch')
}
get arrayFullyFilled() {
return !this.formsArray.controls.some(item => item.errors)
}
get formsArray() {
return this.myForm.get("formsArray") as FormArray;
}
get totalVal() {
return this.myForm.get("totalVal") as FormControl;
}
- 我们还需要修改函数以在 formArray 中添加和删除项目
DelBtn = delIndex => this.formsArray.controls.splice(delIndex, 1);
AddBtn = () => this.formsArray.push(this.fb.control(""));
- 终于可以在html 中实现
formGroup
了
<h2> Form</h2>
<span class='totalVal'>Total Value:{{ totalVal.value }}</span>
<form [formGroup]='myForm'>
<ng-container formArrayName='formsArray'>
<table *ngFor="let item of formsArray.controls; let i = index">
<tr>
<td>
<div>
<label [attr.for]="'fname' + i">Value:</label><br>
<input type="number" [formControlName]="i" type="text" [id]="'fname' + i" name="fname" ><br>
</div>
</td>
<td>
<button type="button" class="btn btn-outline-success"
s (click)="DelBtn(i)" ><span class="fa fa-plus"></span>Delete</button></td>
<tr>
</table>
</ng-container>
<div *ngIf='!sumMismatch && arrayFullyFilled'>
<label for="fname">Reason:</label><br>
<input type="text" id="fname" name="fname" ><br>
</div>
<br>
<button type="button" class="btn btn-outline-success"
(click)="AddBtn()" ><span class="fa fa-plus"></span>Add</button>
<br>
<span class="error" *ngIf="sumMismatch && myForm.touched">Total Value Mismatch</span>
</form>
我已经将 css 提取到自己的文件中
.totalVal {
font-weight: bolder;
font-family: verdana;
}
.btn-outline-success {
border-radius: 40px;
margin-left: 50px;
}
.error {
color: red;
}
编辑 1 - 验证器如何工作?
为了理解这一点,我们来看看我们是如何构建表单组的。我们定义了一个结构,它以
的形式产生一个值 {
totalVal: 100,
formsArray: [''],
reason: ''
}
通过将我们的表单组定义为 this.fb.group({ ... }, {validators: [ sumMatches ] }
具有上述值的表单组将被传递给 sumMatches 函数
在 sumMatches
中,我们将有一个类似 formGroup
的值
{
totalVal: 100,
formsArray: ['50', '20', '10'],
reason: ''
}
在上面,我们使用与formArray
相同的control.get('totalVal').value
从formGroup
中提取100。由于 formArray
值将是一个数组,因此我们可以使用 reduce
函数对其求和。我们最终将其与 return null 进行比较(如果它们匹配)和 Object
(如果匹配)不匹配。
使用上述方法,angular 响应式表单将根据用户提供的内容更新表单 valid
状态的值。因此,我们可以利用此 valid
状态来更新 UI
arrayFullyFilled()
get arrayFullyFilled() {
return !this.formsArray.controls.some(item => item.errors)
}
上面的代码试图找出用户是否已经填充了ALL数组中的输入。在我们的数组中,我们获取所有控件,检查其中是否有错误,如果有错误 return false 否则 return true。考虑到在我的 formGroup
中,我使用 Validators.required
验证
formControls
设为 required
,这是可能的