如何使一个自定义验证器以角度检查基于另一个表单域的表单域?

问题描述

该组件的打字稿是:

export class EtcAddAuthorityComponent implements OnInit {
   addAuthorityForm: FormGroup;
   authTypes: any[] = [];
   loading = false;
   numericRegex = /^[0-9]*$/;

   alphabeticRegex = /^[a-zA-Z]*$/;

   constructor(
      private readonly dialogRef: MatDialogRef<EtcAddAuthorityComponent>,private readonly fb: FormBuilder,private readonly toastr: ToastrService,private readonly ecmService: EcmService,private readonly ecmToolChangeService: EcmToolChangeService,private readonly cgpAlertDialogService: CgpAlertDialogService,@Inject(MAT_DIALOG_DATA) public readonly selectedTool: any
   ) {
      this.addAuthorityForm = this.fb.group({
         authNumber: ['',[Validators.maxLength(20),Validators.pattern(this.alphabeticRegex)]],authNumberN: ['',[Validators.required,Validators.maxLength(20),Validators.pattern(this.numericRegex)]],authTypeName: ['',Validators.maxLength(10)]],authDescription: ['',[Validators.maxLength(500)]]
      });
   }



   ngOnInit() {
      this.loading = true;
      this.ecmService.getAuthTypeCriteria()
         .subscribe({
            next: (res) => {
               if (res) {
                  this.authTypes = res;
               }
            },complete: () => this.loading = false
         });
   }

   onCancelClick() {
      this.dialogRef.close();
   }

   onAddClick() {

      const authNFieldifAuthTypeName = this.authFieldCheck();
      if (authNFieldifAuthTypeName === true) {
         return;
      }
      else {

         const body = {
            ...this.addAuthorityForm.value,partChangeId: this.selectedTool.partChangeId,toolChangeId: this.selectedTool.toolChangeId
         };

         this.loading = true;
         this.ecmToolChangeService.addAuthority(body)
            .subscribe({
               next: (res) => {
                  this.cgpAlertDialogService.showAlertDialog({
                     title: 'Add Authority',message: 'Authority was added successfully!',alert: cgpAlertTypes.success,closeLabel: 'OK'
                  }).afterClosed().subscribe(() => this.dialogRef.close({ reload: true }));
               },error: (err) => {
                  this.loading = false;
                  this.cgpAlertDialogService.showAlertDialog({
                     title: 'Add Authority',message: 'Authority Could not be added. Please try again!',alert: cgpAlertTypes.danger,closeLabel: 'OK'
                  });
               },complete: () => this.loading = false
            });
      }
   }

   authFieldCheck(): boolean {
      const matched: boolean = (this.addAuthorityForm.controls.authTypeName.value === 'EWO') && (!this.addAuthorityForm.controls.authNumber.value);

      if (matched) {
         this.addAuthorityForm.controls.authTypeName.setErrors({
            notFilled: true
         });
      }
      else {
         this.addAuthorityForm.controls.authTypeName.setErrors({ notMatched: false });
      }
      return matched;
   }


}

HTML代码为:

<h1 mat-dialog-title>Add Authority</h1>
<div mat-dialog-content>
   <form class="flex-dialog-container" [formGroup]="addAuthorityForm">
      <mat-form-field>
         <mat-label>Authority #(alpha)</mat-label>
         <input matInput autocomplete="off" formControlName="authNumber" #authNumber>
         <mat-error *ngIf="authNumber.value?.length > 20">Cannot exceed 20 characters.</mat-error>
      </mat-form-field>

      <mat-form-field>
         <mat-label>Authority #(numeric)</mat-label>
         <input matInput autocomplete="off" formControlName="authNumberN" #authNumberN>
         <mat-error *ngIf="addAuthorityForm.controls.authNumberN.hasError('required')">required</mat-error>
         <mat-error *ngIf="authNumberN.value?.length > 20">Cannot exceed 20 characters.</mat-error>   
      </mat-form-field>

      <mat-form-field>
         <mat-label>Authority Type</mat-label>

         <mat-select formControlName="authTypeName">
            <mat-option *ngFor="let at of authTypes" [value]="at.authTypeName">
               {{at.authTypeName}}
            </mat-option>
         </mat-select>
         <mat-error *ngIf="addAuthorityForm.controls.authTypeName.hasError('required')">required</mat-error>
         <mat-error *ngIf=" this.addAuthorityForm.controls.authTypeName.hasError('notFilled')">Authority #(alpha) required</mat-error>
      </mat-form-field>

      <mat-form-field>
         <mat-label>Authority Description</mat-label>
         <input matInput autocomplete="off" formControlName="authDescription" #authDescription>
         <mat-error *ngIf="authDescription.value?.length > 500">Cannot exceed 500 characters.</mat-error>
      </mat-form-field>

   </form>

</div>
<div mat-dialog-actions class="mat-dialog-actions-end no-margin">
   <button mat-raised-button mat-dialog-close cdkFocusInitial (click)="onCancelClick()"
      (keypress.enter)="onCancelClick()">Cancel</button>
   <button mat-raised-button color="primary" (click)="onAddClick()" (keypress.enter)="onAddClick()" [disabled]="addAuthorityForm.invalid">Add</button>
</div>

这是我的添加对话框: The Add dialog box

如何添加自定义验证,以便在“权限类型”下拉列表中选择“ EWO”选项时,如果未输入“ Authority#(Alpha)”,则会显示错误。但是,如果在“权限类型”下拉列表中选择了“ EWO”选项,则应该不会显示任何错误

解决方法

如果您不选择EWO,则可以禁用“ Authority”,因此Angular不会检查是否需要。要禁用/启用,您需要使用方法disable and enable

您可以使用directive to disable/enable控件,订阅valueChanges或在使用mat-select时使用事件selectionChange之类的(*):

<mat-select formControlName="authTypeName"
    (selectionChange)="addAuthorityForm.get('authDescription')
                 [$event.value=='EWO'?'enable':'disable']()">
    <mat-option *ngFor="let at of authTypes" [value]="at.authTypeName">
       {{at.authTypeName}}
    </mat-option>
 </mat-select>

我做了一个simple stackblitz

(*)不要忘记,在创建表单组时,创建启用或禁用的控件

更新,如果我们不想禁用该控件,则确实需要创建一个自定义的Form控件Validation。

我们可以对FormControl,FormGroup或FormArray进行自定义窗体控件验证。在这种情况下,我们选择通过FromControl进行设置。但是我们需要考虑到,如果我们更改了authTypeName,我们需要向Angular指示以检查authDescription是否有效

<mat-select formControlName="authTypeName" 
   (selectionChange)="form.get('authDescription').updateValueAndValidity()">
      ...
</mat-select>

好吧,我们的自定义表单验证。由于我们拥有“控件”,因此在control.parent中我们拥有“表格”,因此就像

  requiredIf(requiredValue:string){
    return (control:FormControl)=>{
      const form=control.parent;
      if (form)
      {
        //really we need decalre controlDescription,it's the
        //same of "control"
        const controlDescription=form.get('authDescription')
        const controlTypeName=form.get('authTypeName')
        if (controlTypeName && controlDescription && 
            controlTypeName.value==requiredValue && !controlDescription.value)
           return {required:true}
      }
      return null
    }
  }

我们可以写

  form=new FormGroup({
    authDescription:new FormControl(null,this.requiredIf('EWO')),authTypeName:new FormControl('EWO')
  })

在声明formGroup时,请确保值'EWO'是固定的

new stackblitz

,

我不确定您说的是什么意思,但“如果为“权限类型”下拉列表选择了“ EWO”选项,则不应显示任何错误。”我假设如果没有为该情况输入'Authority#(Alpha)',它应该不会显示任何错误。

可能有更好的解决方案,但这是我在项目中使用的方法。您可以在表单初始化后放置此块,以便表单已经具有以下身份验证类型和身份验证编号控件可访问:

this.addAuthorityForm.get('authTypeName').valueChanges.subscribe((newValue) => {
    const authNumber = this.addAuthorityForm.get('authNumber');

    // I don't know the exact structure of the authTypeName so you can debug and change the condition if needed
    if (newValue === 'EWO') { 
        authNumber.setValidators([Validators.required,Validators.maxLength(20),Validators.pattern(this.alphabeticRegex)]);
    } else {
        authNumber.setValidators([Validators.maxLength(20),Validators.pattern(this.alphabeticRegex)]);
    }
    authNumber.updateValueAndValidity();
})

基本上,此操作是在auth类型更改时通过添加所需的验证器来为auth号重新分配验证器。我使用Validators.required是因为它是开箱即用的,但是如果您想使其更自定义,则可以使用以下内容:

...
authNumber.setValidators([(c: FormControl) => {
    return c.value ? null : {required: {valid: false}};
},Validators.pattern(this.alphabeticRegex)]);
...

updateValueAndValidity方法是在用户切换身份验证类型时重新验证该字段。

相关问答

Selenium Web驱动程序和Java。元素在(x,y)点处不可单击。其...
Python-如何使用点“。” 访问字典成员?
Java 字符串是不可变的。到底是什么意思?
Java中的“ final”关键字如何工作?(我仍然可以修改对象。...
“loop:”在Java代码中。这是什么,为什么要编译?
java.lang.ClassNotFoundException:sun.jdbc.odbc.JdbcOdbc...