Angular 12 FormGroup动态数组复选框自定义验证器不起作用

发布于 2025-01-24 18:48:45 字数 1826 浏览 3 评论 0原文

我正在创建一个带有材料的Angular 12应用程序。

我有一个从数据库动态加载的复选框数组的表单。

我需要验证至少选择一个复选框,

我在我的oninit()中这样定义了这样的定义:

 ngOnInit(): void {
    this.form = this.fb.group({
      Id: new FormControl(null),
      Name: new FormControl('',Validators.required),
      Recipents: new FormControl('',[Validators.required, matchingEmailValidator()]),
      IsActive: new FormControl(true),
      ProcessorName: new FormControl('',Validators.required),
      Channel: new FormArray([]),
    }, { validators: [customValidateArrayGroup()] }
    );
}

我需要对频道表单阵列进行自定义验证。如果我在频道的定义中添加它,则在检查时不会发射。因此,我决定以形式级别进行。

我补充说:

{ validators: [customValidateArrayGroup()] }

每次对象更改时,都会触发此验证器。

这是我的自定义验证器:

export function customValidateArrayGroup(): ValidatorFn {
  return function validate(formGroup: FormGroup) {
    let checked = 0
    Object.keys(formGroup.controls).forEach(key => {
      const control = formGroup.controls[key]
      if (control.value) {
        checked++
      }
    })

    if (checked < 1) {
      return {
        requireCheckboxToBeChecked: true,
      }
    }
    return null
  }
}

这是我的HTML,我在其中定义了复选框数组的

 <mat-label><strong>Channel</strong></mat-label>
          <li *ngFor="let chanel of notification.NotificationChannelLogLevels">
            <mat-checkbox id= {{chanel.NotificationLogLevel.Id}} formArrayName="Channel"
            [checked]="chanel.IsActive"
             (change)="changeEventFunc($event)">
              {{chanel.NotificationLogLevel.Name}}
            </mat-checkbox>
          </li>

问题是,我遇到的问题是,单击复选框时自定义验证器不会发射。也许是因为它们是在dinam上加载的,并且没有被 formgroup.controls 认识到

我该如何验证?

I am creating an Angular 12 app, with Material.

I have a form with an checkbox array loaded dynamically from database.

I need to validate that at least one checkbox is selected

I defined like this in my OnInit():

 ngOnInit(): void {
    this.form = this.fb.group({
      Id: new FormControl(null),
      Name: new FormControl('',Validators.required),
      Recipents: new FormControl('',[Validators.required, matchingEmailValidator()]),
      IsActive: new FormControl(true),
      ProcessorName: new FormControl('',Validators.required),
      Channel: new FormArray([]),
    }, { validators: [customValidateArrayGroup()] }
    );
}

I need a custom validation for channel form array. If I added it in the definition of the channel, it does not fire when I check it. So, I decided to do it at the form level..

I added:

{ validators: [customValidateArrayGroup()] }

Every time an object changes, it fires this validator.

This is my custom validator:

export function customValidateArrayGroup(): ValidatorFn {
  return function validate(formGroup: FormGroup) {
    let checked = 0
    Object.keys(formGroup.controls).forEach(key => {
      const control = formGroup.controls[key]
      if (control.value) {
        checked++
      }
    })

    if (checked < 1) {
      return {
        requireCheckboxToBeChecked: true,
      }
    }
    return null
  }
}

Here is my Html where I defined the Checkbox Array

 <mat-label><strong>Channel</strong></mat-label>
          <li *ngFor="let chanel of notification.NotificationChannelLogLevels">
            <mat-checkbox id= {{chanel.NotificationLogLevel.Id}} formArrayName="Channel"
            [checked]="chanel.IsActive"
             (change)="changeEventFunc($event)">
              {{chanel.NotificationLogLevel.Name}}
            </mat-checkbox>
          </li>

The problem I have is that the custom validator does not fire when a checkbox is clicked. Maybe is becouse they are loaded dinamically and are not recognized by formGroup.controls

How can I validate this?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

白昼 2025-01-31 18:48:45

在模板中使用formarray和JS数组的混合物很奇怪。当前,您的formarray完全是空的,因此可以预期它在检查复选框时不会运行。您可以选择迭代JS数组,然后按 /删除formarray,然后在接收数据时将值推到formarray,然后在模板中迭代该值。以下解决方案执行后者:

缩短代码。...

构建表格:

this.form = this.fb.group({
  Channel: this.fb.array([], [customValidateArrayGroup()]),
});

我将自定义验证器附加到formarray本身。当您准备好动态数据时,然后迭代它并将表单控件推向您的formarray。我也喜欢使用getter。按下您需要的任何属性,在这里我选择iSactivename仅:

get channels() {
  return this.form.get('Channel') as FormArray;
}

// when you have data accessible:
this.notificationChannelLogLevels.forEach(value => {
  this.channels.push(this.fb.group({
    isActive: value.IsActive,
    name: value.Name
  }))
})

现在在模板中迭代此formarray:

<div formArrayName="Channel">
  <li *ngFor="let chanel of channels.controls; let i = index">
    <ng-container [formGroupName]="i">
      <mat-checkbox formControlName="isActive">
        {{ chanel.get('name').value}}
      </mat-checkbox>
    </ng-container>
  </li>
  <small *ngIf="channels.hasError('hasError') && channels.touched">
    Choose at least one
  </small>
</div>

自定义验证器检查至少一个复选框字段具有iSActive作为true

export function customValidateArrayGroup() {
  return function validate(formArr: AbstractControl): ValidationErrors | null {
    const filtered = (formArr as FormArray).value.filter(chk => chk.isActive);
    return filtered.length ? null : { hasError: true }
  };
}

a stackblitz 供您参考。

You have an odd mix of using formarray and your js array in the template. Currently your formarray is completely empty, so it would be expected that it does not run when checkboxes are checked. You can choose to iterate your JS array and push / remove to formarray, or then you push the values to the formarray when you receive the data and then just iterate that one in the template. The below solution does the latter:

Shortened code....

Build form:

this.form = this.fb.group({
  Channel: this.fb.array([], [customValidateArrayGroup()]),
});

I attached the custom validator to the formarray itself. When you have the dynamic data ready, then iterate it and push form controls to your formarray. I like to use a getter as well. Push whatever properties you need, here I choose IsActive and Name only:

get channels() {
  return this.form.get('Channel') as FormArray;
}

// when you have data accessible:
this.notificationChannelLogLevels.forEach(value => {
  this.channels.push(this.fb.group({
    isActive: value.IsActive,
    name: value.Name
  }))
})

Now iterate this formarray in the template:

<div formArrayName="Channel">
  <li *ngFor="let chanel of channels.controls; let i = index">
    <ng-container [formGroupName]="i">
      <mat-checkbox formControlName="isActive">
        {{ chanel.get('name').value}}
      </mat-checkbox>
    </ng-container>
  </li>
  <small *ngIf="channels.hasError('hasError') && channels.touched">
    Choose at least one
  </small>
</div>

The custom validator checks that at least one checkbox field has isActive as true:

export function customValidateArrayGroup() {
  return function validate(formArr: AbstractControl): ValidationErrors | null {
    const filtered = (formArr as FormArray).value.filter(chk => chk.isActive);
    return filtered.length ? null : { hasError: true }
  };
}

A STACKBLITZ for your reference.

π浅易 2025-01-31 18:48:45

我认为您的模板中有错误的设置。

当需要将每个复选框应用于父容器时,您将应用每个复选框,

<div formArrayName="myFormArray">
  <div *ngFor="*ngFor="let chanel of notification.NotificationChannelLogLevels; let i = index">
    //Use the index here to dynamically tie each mat-checkbox to a FormControl
    <mat-checkbox [FormControl]="myCheckboxes[i]"></mat-checkbox>
  </div>
</div>

然后在.ts文件中将其应用于formarray,并在其内部使用formarray,并在其内部具有表单控件。否则,myCheckbox [i]将是零件的零件或索引。您可以使用添加到表单组中的表单数组,但是必须定义模板中您引用的索引。

这是一篇很好的博客文章,介绍了如何处理表单阵列中的添加/删除实例,

https://netbasal.com/angular-reactive-forms-the-the-------------------------------to-formarray-3adbe6b0b61a

and

​记录级别是静态的,将复选框控件列表定义为FormGroup并将验证器应用于表单组可能更容易或更直观。

I think you have your FormArray setup incorrectly in your template.

You are applying the formArrayName attribute the each checkbox when it needs to be applied to a parent container,

<div formArrayName="myFormArray">
  <div *ngFor="*ngFor="let chanel of notification.NotificationChannelLogLevels; let i = index">
    //Use the index here to dynamically tie each mat-checkbox to a FormControl
    <mat-checkbox [FormControl]="myCheckboxes[i]"></mat-checkbox>
  </div>
</div>

And then in your .ts file you'll have to define myCheckboxes as a FormArray with instances of form control inside it. Otherwise myCheckboxes[i] will be either null or an index out of bounds. You can use the form array you added to your form group, but the indexes you reference in the template have to be defined.

Here is a good blog post going over how to handle adding/removing instances from the form array,

https://netbasal.com/angular-reactive-forms-the-ultimate-guide-to-formarray-3adbe6b0b61a

And another,

https://blog.angular-university.io/angular-form-array/

As a side note, if your logging levels are static, it may just be easier or more intuitive to define the list of checkbox controls as a FormGroup and apply your validator to the form group.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文