Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular material: mat-error not showing despite true methods

I have a confirm password formcontrol that I want to validate.

I want to display my mat-error element when the password is not the same as the value in the confirm password input. For this I have a function called equalPasswords(). If the functions are the same then we receive true, if not, we receive false.

<mat-form-field>
              <input matInput placeholder="Repeat password" [formControl]="password2" type="password">
              <mat-error *ngIf="password2.invalid && password2.hasError('required')">Password is required</mat-error>
              <mat-error *ngIf="!equalPasswords() && !password2.hasError('required')">Passwords need to match</mat-error>
</mat-form-field>

I checked the console and when I enter two different inputs in the password boxes it equalPasswords() returns false. It however still doesn't show the error in the DOM.

Does someone know how to fix this?

Signup.component.ts

@Component({
    selector: 'app-sign-up',
    templateUrl: 'sign-up.component.html',
    styleUrls: ['sign-up.component.css']
})

export class SignUpComponent implements OnInit {

    mySignupForm: FormGroup;
    countries = countries;
    uniqueUsernameMessage;
    uniqueEmailMessage;
    formSubmitted = false;
    @Output() closeSubmenu = new EventEmitter();
    @ViewChild('select') select;

    constructor(
        private authService: AuthenticationService,
        private route: Router,
        private navigationService: NavigationService){}

    get firstName() { return this.mySignupForm.get('firstName'); }
    get lastName() { return this.mySignupForm.get('lastName'); }
    get username() { return this.mySignupForm.get('username'); }
    get email() { return this.mySignupForm.get('email'); }
    get password1() { return this.mySignupForm.get('password1'); }
    get password2() { return this.mySignupForm.get('password2'); }
    get birthDate() { return this.mySignupForm.get('birthDate'); }
    get country() { return this.mySignupForm.get('country'); }
    get house() { return this.mySignupForm.get('house'); }

    ngOnInit() {
        this.mySignupForm = new FormGroup({
            firstName: new FormControl(null, Validators.required),
            lastName: new FormControl(null, Validators.required),
            username: new FormControl(null, [Validators.required, Validators.minLength(5), Validators.maxLength(15)]),
            birthDate: new FormControl(null, Validators.required),
            password1: new FormControl(null, [Validators.required, Validators.minLength(6), Validators.maxLength(15), Validators.pattern('^.*(?=.{4,10})(?=.*\\d)(?=.*[a-zA-Z]).*$')]),
            password2: new FormControl(null, Validators.required),
            email: new FormControl(null, [Validators.required, Validators.email]),
            country: new FormControl(null, Validators.required),
            house: new FormControl(null, Validators.required)
        })
    }

    equalPasswords() {

        console.log('equaltest', this.password1.value === this.password2.value);
        return this.password1.value === this.password2.value;
    }

}
like image 898
tilly Avatar asked Sep 24 '18 13:09

tilly


2 Answers

MatFormField only displays mat-error elements when the FormControl has an error. It does not display just because you tell it to via ngIfElse - that would only work subject to the form control state. One way to solve this problem is to create a custom validator and use it to check that the passwords match. You could also set an error on the field from your equalPasswords() function. That should be something like:

equalPasswords(): boolean {

    const matched: boolean = this.password1.value === this.password2.value;

    console.log('equaltest', matched);

    if (matched) {
        this.signupForm.controls.password2.setErrors(null);
    } else {
        this.signupForm.controls.password2.setErrors({
           notMatched: true
        });
    }

    return matched;
}
like image 167
G. Tranter Avatar answered Oct 18 '22 04:10

G. Tranter


I discovered asubtle situation where this can occur.

If you add a [formGroup] directive on something like a <div> then it will not get submitted == true when the form is submitted (it must be on a <form> for that to occur).

The form being submitted is one of two triggers for displaying the error - the other is for the field to have been touched. And touched means onBlur which means the focus must have been lost.

So you can get this situation:

  • You have [formGroup] on a div

  • You have something like a username / password form.

  • You also have a submit button.

  • You enter both fields and hit enter.

  • Your validation triggers an error but it doesn't show! WHY!

  • Because you never blur-ed the password field (take a look at your cursor - it's still in the password field isn't it!)

  • The easiest solution is to only ever add [formGroup] to a <form> tag

  • The other is to create a custom ErrorStateMatcher

If you are passing a formGroup to a 'dumb' child component via an @Input (that is to a dumb component that doesn't manage its own form) you can use dependency injection and pass in the FormGroupDirective instead in the constructor.

like image 20
Simon_Weaver Avatar answered Oct 18 '22 05:10

Simon_Weaver