Angular 2 / Material - 自定义验证器应用于父FormGroup时不显示md错误

时间:2017-09-20 19:25:29

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

我在使用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-error永远不会出现。因此,要在页面上调试它,我添加了以下内容:

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

不出所料,调试行显示为您所期望的true / false。但是,“md-error”永远不会显示...除非显示“required”错误。我有一种感觉,问题是由于[formControl]引用了passwordConfirmation FormControl,所以如果没有它无效,则不会显示md-error。但是,我希望在外部FormGroup无效时显示此错误。

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

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

3 个答案:

答案 0 :(得分:7)

我发现我能够使用自定义的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时更容易混淆,但我很乐意进行权衡,真正唯一的额外代码是自定义匹配器:)

答案 1 :(得分:0)

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

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

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

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

答案 2 :(得分:0)

这个代码在我的情况下工作得非常好。代码结构如下:

// 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错误消息,其使用方法与上述代码相同。