【问题标题】:Password Confirm Angular Material密码确认角材料
【发布时间】:2018-11-16 14:45:54
【问题描述】:

我正在使用 Angular Material 对用户进行身份验证。我目前正在尝试在确认密码与第一个输入的密码不匹配时显示正确的 mat-error。

这是我的html:

 <mat-form-field hintLabel="Minimum 8 Characters" class="">
                            <mat-label>Password</mat-label>
                            <input 
                            matInput 
                            #input 
                            type="password"
                            formControlName="password">
                            <mat-hint align="end">{{input.value?.length || 0}}/8</mat-hint>
                        </mat-form-field>

                        <mat-form-field>
                            <mat-label>Confirm</mat-label>
                            <input 
                            matInput
                            required  
                            type="password"
                            #confirm
                            formControlName="confirmPassword">
                            <mat-error *ngIf="form.get('confirmPassword').invalid || confirmPasswordMismatch">Password does not match</mat-error>
                            </mat-form-field>

一旦用户专注于密码确认并且不输入任何内容而取消关注,就会显示该错误。一旦用户输入任何内容,即使确认与密码不匹配,错误也会消失。

这是我的 TS 文件:

public get confirmPasswordMismatch() {
        return (this.form.get('password').dirty || this.form.get('confirmPassword').dirty) && this.form.hasError('confirmedDoesNotMatch');
    }

this.form = new FormGroup({
            userName: new FormControl(null, [Validators.required]),
            fullName: new FormControl(null, [Validators.required]),
            email: new FormControl(null, [Validators.required, Validators.pattern(this.EMAIL_REGEX)]),
            password: new FormControl(null),
            confirmPassword: new FormControl(null, ),
        }, (form: FormGroup) => passwordValidator.validate(form));

想要的效果是当用户在确认 pw 为空时将文本输入到 pw 输入时显示错误,并且当两者都有文本但确认与 pw 不匹配时显示错误。

【问题讨论】:

    标签: javascript html angular typescript angular-material


    【解决方案1】:

    我是这样解决的:

    模板:

      <mat-form-field>
        <input matInput type="password" placeholder="Password" formControlName="password" (input)="onPasswordInput()">
        <mat-error *ngIf="password.hasError('required')">Password is required</mat-error>
        <mat-error *ngIf="password.hasError('minlength')">Password must have at least {{minPw}} characters</mat-error>
      </mat-form-field>
    
      <mat-form-field>
        <input matInput type="password" placeholder="Confirm password" formControlName="password2" (input)="onPasswordInput()">
        <mat-error *ngIf="password2.hasError('required')">Please confirm your password</mat-error>
        <mat-error *ngIf="password2.invalid && !password2.hasError('required')">Passwords don't match</mat-error>
      </mat-form-field>
    

    组件:

    export class SignUpFormComponent implements OnInit {
    
      minPw = 8;
      formGroup: FormGroup;
    
      constructor(private formBuilder: FormBuilder) { }
    
      ngOnInit() {
        this.formGroup = this.formBuilder.group({
          password: ['', [Validators.required, Validators.minLength(this.minPw)]],
          password2: ['', [Validators.required]]
        }, {validator: passwordMatchValidator});
      }
    
      /* Shorthands for form controls (used from within template) */
      get password() { return this.formGroup.get('password'); }
      get password2() { return this.formGroup.get('password2'); }
    
      /* Called on each input in either password field */
      onPasswordInput() {
        if (this.formGroup.hasError('passwordMismatch'))
          this.password2.setErrors([{'passwordMismatch': true}]);
        else
          this.password2.setErrors(null);
      }
    }
    

    验证器:

    export const passwordMatchValidator: ValidatorFn = (formGroup: FormGroup): ValidationErrors | null => {
      if (formGroup.get('password').value === formGroup.get('password2').value)
        return null;
      else
        return {passwordMismatch: true};
    };
    

    注意事项:

    • 由于从任一密码字段调用onPasswordInput(),编辑第一个密码字段(从而使密码确认无效)也会导致密码确认字段中显示不匹配错误。
    • 密码确认字段的*ngIf="password2.invalid &amp;&amp; !password2.hasError('required')" 测试确保不会同时显示错误消息(“不匹配”和“必需”)。

    【讨论】:

      【解决方案2】:

      根据您的代码,您似乎没有为 confirmPassword 字段添加任何验证:confirmPassword: new FormControl(null, ) 所以唯一发生的验证是通过 required 属性。此外,如果表单控件无效,则只会在表单字段中显示 mat-error。这意味着您不能仅通过使用 ngIf 来强制显示错误。由于您对该字段只有required 验证,因此只有在该字段为空时才会出现错误是有道理的。要解决这个问题,您需要创建一个验证器用于不匹配检查并将其添加到 confirmPassword 表单控件。作为替代方案,您可以通过添加输入侦听器在字段更改时使用 setErrors() 手动将错误添加到表单控件 - 例如(这只是来自内存 - 可能需要修复):

      <mat-form-field>
          <mat-label>Confirm</mat-label>
          <input matInput required type="password" #confirm formControlName="confirmPassword"
              (input)="onInput($event.target.value)">
          <mat-error *ngIf="form.get('confirmPassword').invalid>
              Password does not match
          </mat-error>
      </mat-form-field>
      
      
      onInput(value) {
          if (this.form.hasError('confirmedDoesNotMatch')) { // or some other test of the value
              this.form.get('confirmPassword').setErrors([{'confirmedDoesNotMatch': true}]);
          } else {
              this.form.get('confirmPassword').setErrors(null);
          }
      }
      

      【讨论】:

      • 谢谢我实际上实现了这个,只要确认是脏的,它就可以工作。当用户开始输入密码而不触摸确认输入时,有没有办法显示错误?
      • 如果你在输入中添加一个自定义的 ErrorStateMatcher,理论上你几乎可以基于任何东西显示一个错误。脏时错误很常见 - 请参阅角度文档here
      【解决方案3】:

      您实际上是在验证表单中的 2 个字段如何相互交互(“密码”和“确认密码”字段)。这称为“跨字段验证

      这意味着,您的自定义验证器不能仅分配给 1 个字段。自定义验证器需要分配给共同的父级表单。

      这是官方记录的最佳实践,用于验证表单中的 2 个字段如何相互交互

      https://angular.io/guide/form-validation#cross-field-validation

      这段代码 sn-p 为我工作

      模板:

      <form method="post" [formGroup]="newPasswordForm">
        <input type="password" formControlName="newPassword" />
        <input type="password" formControlName="newPasswordConfirm" />
        <div class="err-msg" *ngIf="newPasswordForm.errors?.passwordMismatch && (newPasswordForm.touched || newPasswordForm.dirty)">
              confirm-password does not match password
            </div>
      </form>
      

      component.ts:

      export class Component implements OnInit {
          this.newPasswordForm = new FormGroup({
            'newPassword': new FormControl('', [
              Validators.required,
            ]),
            'newPasswordConfirm': new FormControl('', [
              Validators.required
            ])
          }, { validators: passwordMatchValidator });
      }
      
      export const passwordMatchValidator: ValidatorFn = (formGroup: FormGroup): ValidationErrors | null => {
        return formGroup.get('newPassword').value === formGroup.get('newPasswordConfirm').value ?
          null : { 'passwordMismatch': true };
      }
      

      请注意,对于passwordMatchValidator,它位于组件类之外。它不在课堂内

      【讨论】:

        【解决方案4】:

        Henry 的回答非常有帮助(我很惊讶它没有更多的选票),但我对其进行了调整,使其与 Angular Material 控件配合得很好。

        调整依赖于 FormGroup 类具有 parent 属性这一事实。使用此属性,您可以将验证消息绑定到密码确认字段,然后在验证器中引用链。

        public signUpFormGroup: FormGroup = this.formBuilder.group({
          email: ['', [Validators.required, Validators.pattern(validation.patterns.email)]],
          newPassword: this.formBuilder.group({
            password: ['', [
              Validators.required,
              Validators.minLength(validation.passwordLength.min),
              Validators.maxLength(validation.passwordLength.max)]],
            confirmPassword: ['', [Validators.required, passwordMatchValidator]]
          })
        });
        

        验证器如下所示:

        export const passwordMatchValidator: ValidatorFn = (formGroup: FormGroup): ValidationErrors | null => {
          const parent = formGroup.parent as FormGroup;
          if (!parent) return null;
          return parent.get('password').value === parent.get('confirmPassword').value ?
            null : { 'mismatch': true };
        }
        

        然后表格如下所示:

        <div formGroupName="newPassword" class="full-width new-password">
          <mat-form-field class="full-width sign-up-password">
            <mat-label>{{ 'sign-up.password' | translate }}</mat-label>
            <input matInput [type]="toggleSignUpPass.type" [maxlength]="max" [minlength]="min" formControlName="password" required/>
            <mat-pass-toggle-visibility #toggleSignUpPass matSuffix></mat-pass-toggle-visibility>
            <mat-icon matSuffix [color]="color$ | async">lock</mat-icon>
            <mat-hint aria-live="polite">{{ signUpFormGroup.get('newPassword').value.password.length }}
              / {{ max }} </mat-hint>
            <mat-error *ngIf="signUpFormGroup?.get('newPassword')?.controls?.password?.hasError('required')">
              {{ 'sign-up.password-error.required' | translate }}
            </mat-error>
            <mat-error class="password-too-short" *ngIf="signUpFormGroup?.get('newPassword')?.controls?.password?.hasError('minlength')">
              {{ 'sign-up.password-error.min-length' | translate:passwordRestriction.minLength }}
            </mat-error>
            <mat-error *ngIf="signUpFormGroup?.get('newPassword')?.controls?.password?.hasError('maxlength')">
              {{ 'sign-up.password-error.max-length' | translate:passwordRestriction.maxLength }}
            </mat-error>
          </mat-form-field>
          <mat-form-field class="full-width sign-up-confirm-password">
            <mat-label>{{ 'sign-up.confirm-password' | translate }}</mat-label>
            <input matInput [type]="toggleSignUpPassConf.type" formControlName="confirmPassword" required />
            <mat-pass-toggle-visibility #toggleSignUpPassConf matSuffix></mat-pass-toggle-visibility>
            <mat-icon matSuffix [color]="color$ | async">lock</mat-icon>
            <mat-error *ngIf="signUpFormGroup?.get('newPassword')?.controls?.confirmPassword?.hasError('required')">
              {{ 'sign-up.confirm-password-error.required' | translate }}
            </mat-error>
            <mat-error class="password-mismatch" *ngIf="signUpFormGroup?.get('newPassword')?.controls?.confirmPassword?.hasError('mismatch')">
              {{ 'sign-up.password-error.mismatch' | translate }}
            </mat-error>
          </mat-form-field>
        </div>
        

        【讨论】:

        • 你有这个样本的工作 stackBlitz 吗?我努力遵循这一点,但如果条件signUpFormGroup?.get('newPassword')?.controls?.password?.hasError('required') 说:“AbstractControl”类型上不存在属性“控件”,则会在 mat-error 中的模板中出现错误。所以我想我错过了一些东西。 :(
        猜你喜欢
        • 1970-01-01
        • 2015-10-09
        • 2016-09-16
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多