Angular: 存储多个复选框值与相关的文本区域值

Angular: store multiple checkbox values with related textarea value

我有一个表单,其中包含多个 checkbox 和相关的 textarea 字段,用于征求意见。请检查演示 here

当用户先选中复选框然后在评论中输入值时,评论值不会被存储。如果用户先输入评论值,然后选中复选框,然后添加值。

textarea 值为 optional。如果用户选中复选框,则应添加评论。如果选中复选框然后输入评论,则应添加评论。如果先添加评论然后选中复选框,则也应该添加评论。如果取消选中该复选框,则应将其删除。行为应该与 select/unselect 所有选项相同。

我应该怎么做?

Html

<div style="text-align:center">
  <h1>
    {{ title }}
  </h1>
</div>
<div class="container">
  <div class="text-center mt-5">
    <div class="row">
      <div class="col-md-6">
        <ul class="list-group">
          <li class="list-group-item">
            <input
              type="checkbox"
              [(ngModel)]="masterSelected"
              name="list_name"
              value="m1"
              (change)="checkUncheckAll()"
            />
            <strong>Select/ Unselect All</strong>
          </li>
        </ul>
        <ul class="list-group">
          <li class="list-group-item" *ngFor="let item of checklist">
            <input
              type="checkbox"
              [(ngModel)]="item.isSelected"
              name="list_name"
              value="{{ item.id }}"
              (change)="isAllSelected()"
            />
            <textarea [(ngModel)]="item.comment">{{ item.comment }}</textarea>
            {{ item.value }}
          </li>
        </ul>
      </div>
      <div class="col-md-6">
        <code>{{ checkedList }}</code>
      </div>
    </div>
  </div>
</div>

TS

import { Component } from '@angular/core';

@Component({
  selector: 'my-app',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css'],
})
export class AppComponent {
  name = 'Angular';
  masterSelected: boolean;
  checklist: any;
  checkedList: any;

  constructor() {
    this.masterSelected = false;
    this.checklist = [
      { id: 1, value: 'Elenor Anderson', comment: '', isSelected: false },
      { id: 2, value: 'Caden Kunze', comment: 'test', isSelected: true },
      { id: 3, value: 'Ms. Hortense Zulauf', comment: '123', isSelected: true },
      { id: 4, value: 'Grady Reichert', comment: '', isSelected: false },
      { id: 5, value: 'Dejon Olson', comment: '', isSelected: false },
      { id: 6, value: 'Jamir Pfannerstill', comment: '', isSelected: false },
      { id: 7, value: 'Aracely Renner DVM', comment: '', isSelected: false },
      { id: 8, value: 'Genoveva Luettgen', comment: '', isSelected: false },
    ];
    this.getCheckedItemList();
  }

  // The master checkbox will check/ uncheck all items
  checkUncheckAll() {
    for (var i = 0; i < this.checklist.length; i++) {
      this.checklist[i].isSelected = this.masterSelected;
    }
    this.getCheckedItemList();
  }

  // Check All Checkbox Checked
  isAllSelected() {
    this.masterSelected = this.checklist.every(function (item: any) {
      return item.isSelected == true;
    });
    this.getCheckedItemList();
  }

  // Get List of Checked Items
  getCheckedItemList() {
    this.checkedList = [];
    for (var i = 0; i < this.checklist.length; i++) {
      if (this.checklist[i].isSelected)
        this.checkedList.push(this.checklist[i]);
    }
    this.checkedList = JSON.stringify(this.checkedList);
  }
}

请多多指教

因为我有一些时间,我想我会为你写一个可能的解决方案......在评论中提到,我会建议一个表单,因为如果 textarea 有价值,你需要复选框。这是一个被动的方法。

我已经创建了一个表单组,你不一定需要它,你也可以只使用一个表单数组,而不需要父表单。

无论如何,在这种情况下,我声明一个带有 formarray 的表单组,我们将在其中存储我们的值,然后我们根据您的数组填充 formarray。我们监听每个文本区域的变化,如果它有值,我们设置 Validators.requiredTruerequiredTrue 用于复选框),否则我们用 clearValidators 清除它。最后我们用 updateValueAndValidity.

更新表单控件的值和有效性

我们可以使用单独的表单控件来选中/取消选中所有复选框,我在这里将其命名为 masterSwitch ;)

我们监听该字段的变化并更新表单数组中的值,如果值为 true.

则清除任何可能的 requiredTrue 验证器

所以上面的解释转化为下面的TS代码:

  myForm!: FormGroup;
  alive = true;

  // our unselect / select all field
  masterSwitch = new FormControl(false);

  checklist = [....];

  constructor(private fb: FormBuilder) {
    this.myForm = this.fb.group({
      checklist: this.fb.array([]),
    });
    // add values to formarray
    this.populate();

    // listen to all comment formcontrols and add / remove required validator
    merge(
      ...this.formChecklist.controls.map(
        (control: AbstractControl, index: number) =>
          control.get('comment').valueChanges.pipe(
            tap((value) => {
              const isSelected = control.get('isSelected');
              if (value.trim()) {
                isSelected.setValidators(Validators.requiredTrue);
              } else {
                isSelected.clearValidators();
              }
              isSelected.updateValueAndValidity();
            })
          )
      )
    )
      .pipe(takeWhile((_) => this.alive))
      .subscribe();

    // listen to the select / unselect all and toggle checkbox as well as handle validators
    this.masterSwitch.valueChanges
      .pipe(takeWhile((_) => this.alive))
      .subscribe((value: boolean) => {
        this.formChecklist.controls.forEach((ctrl: FormGroup) => {
          ctrl.patchValue({ isSelected: value });
          const isSelected = ctrl.get('isSelected');
          if (value) {
            isSelected.clearValidators();
          } else {
            if (ctrl.get('comment').value) {
              isSelected.addValidators(Validators.requiredTrue);
              isSelected.updateValueAndValidity();
            }
          }
        });
      });
  }

  get formChecklist() {
    return this.myForm.get('checklist') as FormArray;
  }

  populate() {
    this.checklist.forEach((x) => {
      this.formChecklist.push(
        this.fb.group({
          id: x.id,
          value: x.value,
          comment: x.comment,
          isSelected: x.isSelected,
        })
      );
    });
  }

  ngOnDestroy() {
    this.alive = false;
  }

在模板中,我们只是做通常的响应式表单,通过 formarray 循环并显示表单控件,没有什么特别的:

<label>
  <input type="checkbox" [formControl]="masterSwitch" />
  Uncheck / Check all
</label>
<hr>
<form [formGroup]="myForm" (ngSubmit)="onSubmit()">
  <div formArrayName="checklist">
    <div *ngFor="let item of formChecklist.controls; let i = index">
      <div [formGroupName]="i">
        <label>
          <input type="checkbox" formControlName="isSelected" />
          {{ item.get('value').value }}
        </label>
        <textarea formControlName="comment"></textarea>
        <small *ngIf="item.get('isSelected').hasError('required')"
          >Checkbox Required!</small
        >
      </div>
    </div>
  </div>
  <button>Submit</button>
</form>

并且在提交表单时,您可以过滤掉未选中的表单数组中的对象,我没有在此处包含它,因为它是基本的 js filter :)

最后...一个STACKBLITZ供大家参考

双向绑定工作正常,注释更改正确存储在您在 属性 checklist

中的对象中

您看不到应用程序中打印的更改,因为在过滤后的 属性 checkedList 中,您正在 字符串化 过滤后的数组(我猜测打印它)。

如果您在方法 getCheckedItemList() 中删除行 this.checkedList = JSON.stringify(this.checkedList);

并使用 json 管道 <code>{{ checkedList | json }}</code> 在模板中打印 属性,您会看到更改已正确存储。

干杯

只需将 (input)="getCheckedItemList()" 添加到您的 textarea

demo