MatError & Cross-Field Validators In Angular Material 7
NOTE: The content is relevant to Angular Material 2+.
The title says Angular Material 7, since it’s the latest version out, and it’s intended to attract attention (we always want to read up-to-date information).
It seems like a basic necessity to be able to validate a form by data which is currently in the form.
Take the most basic and common example: a Verify Password field in a sign-up form.
Assuming you’re using Angular Material to create a form like the one above, you might get into the following situation:
Don’t you worry, it’s not a bug, it’s just the way Angular Material works.
Let’s learn about it, and then see how we can get to a working solution.
NOTE: This post is assuming you’re familiar with some concepts of Angular.
I will not teach you them here in the post. I will, however, give you a link for you to learn from.
Matching Passwords Validator
We first want to create a form with what’s called a “cross-field” validation.
What this means is that we want to validate data in our form, based on other data — in the same form.
For this, I will use Reactive Forms (yes, I love it), using the out-of-the-box utility FormBuilder. And then I’ll create the password validator.
Look at the last line. See that we put the validator in the form level, and not in the control level?
This is how we do cross-fields validation. They apply to the form and not to a control.
Which means..
Our control is always valid!
mat-error Not Being Displayed
Let’s have a look at the html we’re using for the input
Now, I’m thinking that once the passwords do not match, we should be seeing the mat-error. Correct?
That is not the state if we’re using cross-field validation — and we are.
Why is this happening?
Let’s look at a snippet from the official code of mat-form-field.
We can learn from this that only when the form-field (the control) says it has errors, it displays the mat-error
using a transclusion.
So, it doesn’t matter if we even try to put the mat-error
without any conditions, it won’t show until the control states that it has issues (validity or mentally 😅)
ErrorStateMatcher To The Rescue!
According to the official documentation and confirming by looking at the source code, errors in the control are shown if the control is invalid AND either touched or the form is submitted.
In order to make this work for us, we need to make the errors show if the form itself is invalid (since we’re talking about a form-level validation).
In that case, we need to define our own ErrorStateMatcher
for the control.
An ErrorStateMatcher
is a class that defines for the form-field when to display error messages.
It’s simple to create one. It will enforce us to create a isErrorState
method which receives a FormControl
and FormGroupDirective
(reactive)/ NgForm
(template-driven) arguments. Those are supplied by the form-field itself, so all we need to do is just work with them.
We’re saying now — If we started typing something in the input, and the form itself is invalid, state that the control has errors.
Let’s add this ErrorStateMatcher
to the input:
Now, if we type mis-matching passwords we should see the error, and instantly!
Demo
Here’s the demo for this.
Go ahead and play with it.