用逻辑复制表格

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, ... ],
  • 注入FormBuilderclass
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;
}

See this Demo

编辑 1 - 验证器如何工作?

为了理解这一点,我们来看看我们是如何构建表单组的。我们定义了一个结构,它以

的形式产生一个值
  {
    totalVal: 100,
    formsArray: [''],
    reason: ''
  }

通过将我们的表单组定义为 this.fb.group({ ... }, {validators: [ sumMatches ] } 具有上述值的表单组将被传递给 sumMatches 函数

sumMatches 中,我们将有一个类似 formGroup 的值

  {
    totalVal: 100,
    formsArray: ['50', '20', '10'],
    reason: ''
  }

在上面,我们使用与formArray相同的control.get('totalVal').valueformGroup中提取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,这是可能的