【问题标题】:How to validate that at least one checkbox should be selected?如何验证应选择至少一个复选框?
【发布时间】:2017-09-09 03:15:08
【问题描述】:

我想在这里对没有表单标签的复选框进行验证。 至少应选中一个复选框。

<div *ngFor="let item of officeLIST">
  <div *ngIf=" item.officeID == 1">
    <input #off type="checkbox" id="off" name="off" value="1" [(ngModel)]="item.checked">
    <label>{{item.officename}}</label>
  </div>

  <div *ngIf="item.officeID== 2">
    <input #off type="checkbox" id="off" name="off" value="2" [(ngModel)]="item.checked">
    <label>{{item.officename}}</label>
  </div>

  <div *ngIf="item.officeID== 3">
    <input #off type="checkbox" id="off" name="off" value="3" [(ngModel)]="item.checked">
    <label>{{item.officename}}</label>
  </div>
</div>

对于其他字段,我将输入 required 并执行 error|touched|valid 等,但由于复选框不是单个输入,我不能在每个复选框中都输入 required,因为所有复选框都必须检查。那么如何进行验证以提醒用户至少应该检查一个?

【问题讨论】:

  • 你没有使用任何表单标签,通过验证的时候要验证?喜欢on click of some button should say nothing selected
  • 我想在没有选中单个复选框时进行验证。例如:名称:文本框,当用户单击它但未输入任何内容时,应显示“需要名称”。复选框也是如此

标签: forms angular validation typescript


【解决方案1】:

接受的答案滥用了一些东西,以不应该的方式使用。使用reactive forms,最好、最简单且可能是正确的方法是使用FormGroup 来保存您的分组复选框并创建一个验证器来检查该组中是否至少有一个(或多个)复选框被选中。

为此,只需在现有的 FormGroup 中创建另一个 FormGroup 并将验证器附加到它:

form = new FormGroup({
    // ...more form controls...
    myCheckboxGroup: new FormGroup({
      myCheckbox1: new FormControl(false),
      myCheckbox2: new FormControl(false),
      myCheckbox3: new FormControl(false),
    }, requireCheckboxesToBeCheckedValidator()),
    // ...more form controls...
  });

这里是验证器。我这样做是为了让您甚至可以使用它来检查是否至少选中了 X 个复选框,例如requireCheckboxesToBeCheckedValidator(2):

import { FormGroup, ValidatorFn } from '@angular/forms';

export function requireCheckboxesToBeCheckedValidator(minRequired = 1): ValidatorFn {
  return function validate (formGroup: FormGroup) {
    let checked = 0;

    Object.keys(formGroup.controls).forEach(key => {
      const control = formGroup.controls[key];

      if (control.value === true) {
        checked ++;
      }
    });

    if (checked < minRequired) {
      return {
        requireCheckboxesToBeChecked: true,
      };
    }

    return null;
  };
}

不要忘记在您的模板中添加指令“formGroupName”来包装您的复选框。不过不用担心,如果您忘记了,编译器会用错误消息提醒您。然后,您可以像在 FormControl 上一样检查复选框组是否有效:

<ng-container [formGroup]="form">
   <!-- ...more form controls... -->

   <div class="form-group" formGroupName="myCheckboxGroup">
      <div class="custom-control custom-checkbox">
        <input type="checkbox" class="custom-control-input" formControlName="myCheckbox1" id="myCheckbox1">
        <label class="custom-control-label" for="myCheckbox1">Check</label>
      </div>

      <div class="custom-control custom-checkbox">
        <input type="checkbox" class="custom-control-input" formControlName="myCheckbox2" id="myCheckbox2">
        <label class="custom-control-label" for="myCheckbox2">At least</label>
      </div>

      <div class="custom-control custom-checkbox">
        <input type="checkbox" class="custom-control-input" formControlName="myCheckbox3" id="myCheckbox3">
        <label class="custom-control-label" for="myCheckbox3">One</label>
      </div>

      <div class="invalid-feedback" *ngIf="form.controls['myCheckboxGroup'].errors && form.controls['myCheckboxGroup'].errors.requireCheckboxesToBeChecked">At least one checkbox is required to check</div>
    </div>

    <!-- ...more form controls... -->
  </ng-container>

*这个模板是非常静态的。当然,您可以通过使用包含表单数据(FormControl 的键、标签、必需等)的附加数组来动态创建它,并使用 ngFor 自动创建模板。

请不要在接受的答案中滥用隐藏的 FormControl。 FormControl 并不意味着存储 id、标签、帮助文本等数据,甚至没有名称/键。所有这些,以及更多,都应该单独存储,例如通过一个规则的对象数组。 FormControl 只保存一个输入值并提供所有这些很酷的状态和功能。

我创建了一个工作示例,您可以使用:https://stackblitz.com/edit/angular-at-least-one-checkbox-checked

【讨论】:

  • 一件事:{ validators: requireCheckboxesToBeCheckedValidator() } - 其余的都可以
  • @Sergej FormGroup 构造函数接受 ValidatorFn 或 ValidatorFn[]。或者你是什么意思?
  • 我的意思是验证器必须是带有validators 键的对象。你在你的例子中错过了它。 FormGroup 构造函数中的第二个参数。 angular.io/api/forms/FormGroup 您必须将它与将验证器本身作为参数的FormControl 混淆。
  • 这只是部分正确。 FormGroup 的定义说第二个参数的类型是ValidatorFn | AbstractControlOptions | ValidatorFn[]。所以是的,你可以使用AbstractControlOptions,就像你提到的那样,或者你可以像我一样直接使用ValidatorFn,或者ValidatorFn的数组
  • 如果有人需要调整此解决方案以使用复选框的动态列表,诀窍是在 subscribe 调用中添加内部 FormGroup。喜欢:getCheckboxes().subscribe(result =&gt; { const formControls = {}; result.forEach(i =&gt; formControls['myCheckbox' + i.id] = new FormControl(false)); this.form.addControl('myCheckboxGroup', new FormGroup(formControls, requireCheckboxesToBeCheckedValidator())); }
【解决方案2】:

考虑创建一个包含您的复选框组的FormGroup,并将该组的选中值绑定到具有所需验证器的隐藏表单控件。

假设您有三个复选框

items = [
  {key: 'item1', text: 'value1'},      // checkbox1 (label: value1)
  {key: 'item2', text: 'value2'},      // checkbox2 (label: value2)
  {key: 'item3', text: 'value3'},      // checkbox3 (label: value3)
];

第 1 步:为您的复选框定义 FormArray

let checkboxGroup = new FormArray(this.items.map(item => new FormGroup({
  id: new FormControl(item.key),      // id of checkbox(only use its value and won't show in html)
  text: new FormControl(item.text),   // text of checkbox(show its value as checkbox's label)
  checkbox: new FormControl(false)    // checkbox itself
})));

*易于通过 ngFor

显示

Step2:创建一个隐藏的必需formControl来保持复选框组的状态

let hiddenControl = new FormControl(this.mapItems(checkboxGroup.value), Validators.required);
// update checkbox group's value to hidden formcontrol
checkboxGroup.valueChanges.subscribe((v) => {
  hiddenControl.setValue(this.mapItems(v));
});

我们只关心隐藏控件所需的验证状态,不会在 html 中显示这个隐藏控件。

Step3:创建最终表单组,包含下面的复选框组和隐藏的表单控件

this.form = new FormGroup({
  items: checkboxGroup,
  selectedItems: hiddenControl
});

HTML 模板:

<form [formGroup]="form">
  <div [formArrayName]="'items'" [class.invalid]="!form.controls.selectedItems.valid">
    <div *ngFor="let control of form.controls.items.controls; let i = index;" [formGroup]="control">
      <input type="checkbox" formControlName="checkbox" id="{{ control.controls.id.value }}">
      <label attr.for="{{ control.controls.id.value }}">{{ control.controls.text.value }}</label>
    </div>
  </div>
  <div [class.invalid]="!form.controls.selectedItems.valid" *ngIf="!form.controls.selectedItems.valid">
    checkbox group is required!
  </div>
  <hr>
  <pre>{{form.controls.selectedItems.value | json}}</pre>
</form>

参考这个demo。

【讨论】:

  • 更好的答案恕我直言
  • 您好,感谢您的回答,但我只是好奇-是否有必要为每个复选框实例化 3 个 FormControls 以创建“必需的”可验证(不认为这是一个词)复选框列表?这似乎确实有效,我会稍微弄乱它,但我觉得这里有一些不必要的开销......你觉得呢?
  • @AdamPlocher 抱歉之前的回答不正确,我现在可以考虑您的问题。此处3 instance per checkbox 的原因是FormControl 本身不支持为复选框保留id 和文本等附加信息。
  • @AdamPlocher 您不应滥用 FormControl 将数据存储为它们的值(此处为键和标签)。这是在 Angular 中思考反应形式的一种非常错误的方式。 FormGroup 包含 FormControl 及其验证器、当前值和当前状态,仅此而已。这不是一个好的答案。创建一个附加的、隐藏的复选框(步骤 2)的总体思路还不错,但是为整个 FormGroup 创建一个验证器要容易得多。无需观看 valueChanges。顺便说一句。需要它是错误的,它应该有验证器 requiredTrue。
  • 该解决方案请参阅my answer
【解决方案3】:

我遇到了同样的问题,这是我最终使用 Angular 6 FormGroup 的解决方案,因为我的复选框很少。

HTML 注意:我使用 Angular Material 进行样式设置,根据需要进行更改。

<form [formGroup]="form">
  <mat-checkbox formControlName="checkbox1">First Checkbox</mat-checkbox>
  <mat-checkbox formControlName="checkbox2">Second Checkbox</mat-checkbox>
  <mat-checkbox formControlName="checkbox3">Third Checkbox</mat-checkbox>
</form>

TypeScript

form: FormGroup;

constructor(private formBuilder: FormBuilder){}

ngOnInit(){

  this.form = this.formBuilder.group({
    checkbox1: [''],
    checkbox2: [''],
    checkbox3: [''],
  });

  this.form.setErrors({required: true});
  this.form.valueChanges.subscribe((newValue) => {
    if (newValue.checkbox1 === true || newValue.checkbox2 === true || newValue.checkbox3 === true) {
      this.form.setErrors(null);
    } else {
      this.form.setErrors({required: true});
    }
  });
}

基本上,订阅表单中的任何更改,然后根据新的表单值根据需要修改错误。

【讨论】:

  • 给整个 FormGroup 一个验证器来检查是否至少有一个复选框被选中要容易得多。无需观看 valueChanges。
  • @Mick 看了你的回答后我同意你的看法,这是我当时想出的解决方案。我赞成您的答案,因为它看起来更高效且可重用,并将在未来的开发中使用该模型,因为我认为这是恕我直言的最佳答案。谢谢!
【解决方案4】:

在验证时(例如某些点击事件)遍历您的数组并检查至少一项是否为真。

let isSelected: any = this.officeLIST.filter((item) => item.checked === true);
if(isSelected != null && isSelected.length > 0) {
 //At least one is selected
}else {
 alert("select at least one");
}

【讨论】:

  • 嗨 nugu,我应该在我的构造函数 ngOnIt 中执行此代码吗?
  • oo 说我的点击,update()(进入更新功能,所以我应该检查那里?)
  • 是的,理想情况下单击按钮即可:)
【解决方案5】:

将 (ngModelChange)="onChange(officeLIST)" 添加到您的复选框,并在您的 .ts 文件中包含以下代码。

onChange(items) {
    var found = items.find(function (x) { return x.checked === true; });
    if (found)
      this.isChecked = true;
    else
      this.isChecked = false;
  }

在任何你想要的地方使用 isChecked 变量。

【讨论】:

    【解决方案6】:

    我实现了与 Mick 提出的当前接受版本类似的解决方案(使用 FormGroup 和自定义验证器),但如果您像我一样不需要处理检查数量 > 0 时显示错误,您可以大大简化验证器:

    export function checkboxGroupValidator(): ValidatorFn {
      return (formGroup: FormGroup) => {
        const checkedKeys = Object.keys(formGroup.controls).filter((key) => formGroup.controls[key].value);
    
        if (checkedKeys.length === 0) { return { requireCheckboxesToBeChecked: true }; }
    
        return null;
      };
    }
    

    【讨论】:

    【解决方案7】:

    您应该检查表单元素的触摸和脏状态

    <form #myForm="ngForm"  *ngIf="active"  (ngSubmit)="onSubmit()">
    
        <div class="form-group">
            <label for="name">Name</label>
            <input type="text" id="name" class="form-control"
                   required name="name" [(ngModel)]="myform.name"
                   #name="ngModel" >
            <div *ngIf="name.errors && (name.dirty || name.touched)"
                 class="alert alert-danger">
                <div [hidden]="!name.errors.required">
                  Name is required
                </div>
            </div>
        </div>
    
    </form>
    

    您可以将前面的答案和我的答案结合起来处理这两种情况

    【讨论】:

    • 你能说出officeLIST对象的准确json格式吗
    猜你喜欢
    • 2011-03-13
    • 2015-11-06
    • 1970-01-01
    • 1970-01-01
    • 2017-07-27
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-07-30
    相关资源
    最近更新 更多