【问题标题】:Angular 2 / Material - md-error not displayed with custom validator applied to parent FormGroupAngular 2 / Material - 应用到父 FormGroup 的自定义验证器未显示 md 错误
【发布时间】:2018-03-02 00:01:12
【问题描述】:

我在使用 Angular (v4.3.6) 在模型驱动表单上显示验证错误时遇到了一些麻烦。

在我的模型中,我有以下内容:

this.registerForm = formBuilder.group({
        'email':[null,Validators.compose([Validators.required, ValidateEmail])],
        'firstName':[null, Validators.required],
        'lastName':[null, Validators.required],
        'passwordGroup': formBuilder.group({
            'password':[null, Validators.compose([Validators.required,Validators.minLength(8)])],
            'passwordConfirmation':[null, Validators.required],
        },{validator: ValidatePasswordConfirmation})
    });

引用的ValidatePasswordConfirmation自定义验证器如下:

export function ValidatePasswordConfirmation(group: FormGroup) {

    if(group.value.password !== group.value.passwordConfirmation){
        return { 'no-match':true };
    }

    return null;
}

最后,在我的模板中,我有以下内容:

<md-form-field>
  <input mdInput name="passwordConfirmation" placeholder="Confirm password" [formControl]="registerForm.controls['passwordGroup'].controls['passwordConfirmation']" [(ngModel)]="model.passwordConfirmation" type="password">

<md-error *ngIf="registerForm.controls['passwordGroup'].controls['passwordConfirmation'].hasError('required')">
    Password confirmation is required
  </md-error>

<md-error *ngIf="registerForm.controls['passwordGroup'].hasError('no-match')">
    Passwords don't match
  </md-error>

</md-form-field>

但是,由“不匹配”错误控制的 md 错误永远不会出现。因此,为了在页面上进行调试,我添加了以下内容:

no-match = {{registerForm.controls['passwordGroup'].hasError('no-match')}} 
invalid = {{registerForm.controls['passwordGroup'].invalid}}

不出所料,调试行显示真/假,如您所料。但是,永远不会显示“md-error”……除非显示“必需”错误。我有一种感觉,问题是由于 [formControl] 引用了 passwordConfirmation FormControl,因此如果它无效,则不会显示 md 错误。但是,我希望在外部 FormGroup 无效时显示此错误。

任何关于我在哪里出错的指示都会非常有帮助!

最后,我尝试了其他一些方法,例如在 PasswordConfirmation FormControl 上设置错误,这可行,但我想知道为什么我当前的实现失败了。

【问题讨论】:

    标签: angular2-forms angular-material2 angular-validation


    【解决方案1】:

    我发现我能够使用自定义 ErrorStateMatcher 解决完全相同的问题(默认的要求控件在显示任何错误之前处于无效状态)

    export class ParentErrorStateMatcher implements ErrorStateMatcher {
        isErrorState(control: FormControl | null, form: FormGroupDirective | NgForm | null): boolean {
            const isSubmitted = !!(form && form.submitted);
            const controlTouched = !!(control && (control.dirty || control.touched));
            const controlInvalid = !!(control && control.invalid);
            const parentInvalid = !!(control && control.parent && control.parent.invalid && (control.parent.dirty || control.parent.touched));
    
            return isSubmitted || (controlTouched && (controlInvalid || parentInvalid));
        }
    }
    

    这在我的页面组件上作为变量公开,就像这样......

    @Component({
        templateUrl: 'register.page.component.html',
        styleUrls: ['register.page.styles.css']
    })
    export class RegisterPageComponent implements OnInit {
        registerForm: FormGroup;
        parentErrorStateMatcher = new ParentErrorStateMatcher();
    
        // Accessors
        get name() { return this.registerForm.get('name'); }
        get email() { return this.registerForm.get('email'); }
        get passwords() { return this.registerForm.get('passwords'); }
        get password() { return this.registerForm.get('passwords.password'); }
        get confirmPassword() { return this.registerForm.get('passwords.confirmPassword'); }
    ...
    

    有了这个表格...

    this.registerForm = this.formBuilder.group({
            name: ['', [
                Validators.required,
                Validators.maxLength(256)]
            ],
            email: ['', [
                Validators.email,
                Validators.required,
                Validators.maxLength(256)]
            ],
            passwords: this.formBuilder.group({
                password: ['', [
                    Validators.required,
                    Validators.maxLength(128)
                ]],
                confirmPassword: ['', [
                    Validators.required
                ]]
            },
                {
                    validator: CustomValidators.doNotMatch('password', 'confirmPassword')
                }),
        });
    

    还有密码字段的这个标记(参见 errorStateMatcher 和 confirmPassword 输入控件的最后一个 mat-error)...

    <div formGroupName="passwords">
        <mat-form-field class="full-width">
            <input matInput placeholder="Password" type="password" name="password" id="password" formControlName="password" required/>
            <mat-error *ngIf="password.errors && password.errors.required">
                Please enter your password
            </mat-error>
            <mat-error *ngIf="password.errors && password.errors.maxLength">
                Password must be less than 128 characters long
            </mat-error>
        </mat-form-field>
    
        <mat-form-field class="full-width">
            <input matInput placeholder="Confirm Password" type="password" name="confirmPassword" id="confirmPassword" formControlName="confirmPassword" required
                [errorStateMatcher]="parentErrorStateMatcher"/>
            <mat-error *ngIf="confirmPassword.errors && confirmPassword.errors.required">
                Please confirm your password
            </mat-error>
            <mat-error *ngIf="passwords.errors && passwords.errors.doNotMatch">
                Passwords do not match
            </mat-error>
        </mat-form-field>
    </div>
    

    感觉比我不使用 Material 时更加混乱,但我很高兴能够权衡取舍,而且真正唯一的额外代码是自定义匹配器 :)

    【讨论】:

      【解决方案2】:

      在我的情况下,这段代码运行良好。代码结构如下:

      // component.ts
      import { FormBuilder, FormGroup, FormControl, Validators } from '@angular/forms';
      
      function validate(v: FormGroup): any {
        let a = k.get('a');
        let b = k.get('b');
        if (a.value != undefined && (b.value == undefined || a.value == "none" || b.value == " ")) {
          return { ab: true };
        }
        return null;
      }
      
      @Component({
      })
      
      export class component implements OnInit, OnDestroy {
        constructor(){}
        ngOnInit() {
          this.registerForm = formBuilder.group({
              'email':[null,Validators.compose([Validators.required, ValidateEmail])],
              'firstName':[null, Validators.required],
              'lastName':[null, Validators.required],
              'passwordGroup': formBuilder.group({
                  'password':[null, Validators.compose([Validators.required,Validators.minLength(8)])],
                  'passwordConfirmation':[null, Validators.required],
              },{validator: ValidatePasswordConfirmation})
          });
        }
      }
      

      如果条件出现 HTML 错误消息显示,其使用方式与上述代码中相同。

      【讨论】:

      • 可以包含你的 html 吗?
      • HTML
        错误信息
      【解决方案3】:

      我已经在 Angular Material 输入上使用 },{validator: ValidatePasswordConfirmation} 等自定义验证遇到了这个问题,这似乎是 Material 的问题。这是我在 Angular Material 页面上的问题:https://github.com/angular/material2/issues/7084。它没有答案,所以问题仍然存在。我的决定是在提交表单后使用验证,它不是很漂亮和正确,但它正在工作。希望下个版本能解决这个问题。

      或者,如果您想要实时验证,您可以执行以下操作:

      checkArePasswordsValid() {
          this.passwordsAreEqual = this.passwordValue === this.passwordConfirmationValue ? true : false;
      }
      

      并使用此布尔值进行验证。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2023-03-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多