Validation in Angular (v2+), various approaches, various APIs to use. We’re going to use
If the above makes no sense, go here then drop back! When our
First, let’s look at this line:
This means we can pass a
So, what do the internals look like?
The first line of code we completely know already, it’s just the syntax from above. Now, what is this
Interesting, it checks the presence of the
Which is equivalent to:
We can pass a second argument (or third, for
We won’t dive into the specifics of
Let’s circle back around and take a look at our original piece of code:
What we’re going to add is a custom validator that ensures when our lovely fake users sign up to our fake form, that their
We’ll add this inside
Next step, we’ll inject
So, we know now that
Incidentally, we can also use this same API when implementing custom validators, so referencing our previous form group code, in which we have nested
At this point,
So until now, this means that if everything is working perfectly, we’ll return no errors. Now we need to add that custom validation.
Ignoring the HTML, we’re interested specifically in this piece:
This means that we want to be able to query the
We can condense this nicely onto a one line ternary, final code:
Now, we import our validator, and add it to the second argument of the
AbstractControl
to learn how to validate a particular FormGroup
. I covered FormGroup
, FormControl
and FormBuilder
in my previous reactives form fundamentals article - which I’d recommend checking out before this one if you’re new to Angular forms.
Table of contents
What is a FormGroup?
Covered in the previous article, but we’ll whip up a quick sample real quick to use for the rest of this post:If the above makes no sense, go here then drop back! When our
FormBuilder
, i.e. the fb
injected FormBuilder
instantiates new groups through this.fb.group()
, each of those is technically a new FormGroup()
. So when we refer to “FormGroups”, this is what we’re talking about from here on out.FormBuilder/FormGroup source code
Before we can learn “how to do custom validation”, we must dive into the workings of the APIs first to see what’s happening and actually have some idea what’s going on, so let’s do that real quick. Here’s the syntax for theFormBuilder
class:First, let’s look at this line:
This means we can pass a
controlsConfig
Object down into the FormBuilder
. This is what happens when we call this.fb.group()
. We also have an optional extra?
property, and finally : FormGroup
, which is the return value. So essentially, FormBuilder
is just an abstraction/wrapper at this point.So, what do the internals look like?
The first line of code we completely know already, it’s just the syntax from above. Now, what is this
extra
argument that’s being passed in? Here’s where it’s used:Interesting, it checks the presence of the
extra
“thing”, and providing it’s there and is in fact an Object, it’ll grab the validator
property from it. Which means that the extra
thing which is the optional second function argument, in fact looks like this when creating a group()
with FormBuilder
:Which is equivalent to:
We can pass a second argument (or third, for
asyncValidator
) that gets passed to new FormGroup()
instance. One more thing before we implement validation, we’ll see how FormGroup
handles this internally:FormGroup
actually extends AbstractControl
and then passes validator
and asyncValidator
to the AbstractControl
through the super()
call, which calls the constructor
of the parent abstract class.We won’t dive into the specifics of
AbstractControl
, but we know that it’s essentially the mothership of our form that sets, controls, and powers all things such as dirty
, pristine
, touched
and other funky abstract methods we can touch when we ask the AbstractControl
.AbstractControl
This next section will give you an insight onAbstractControl
, however using AbstractControl
is not essential in this case to implementing our custom FormGroup
validation, as we can also inject FormGroup
to talk to our form controls also - but this means the “control” that’s injected needs to be a FormGroup
instance, so we can use AbstractControl
instead for consistency.Let’s circle back around and take a look at our original piece of code:
What we’re going to add is a custom validator that ensures when our lovely fake users sign up to our fake form, that their
email
and confirm
email fields both match up. Using AbstractControl
we can do this, but first, we need to actually compose the validation function:We’ll add this inside
email-matcher.ts
for the sake of breaking code up into different files. This will allow us to then inject it into our emailMatcher
validator into our FormGroup
or FormBuilder
wrapper.Next step, we’ll inject
AbstractControl
:So, we know now that
AbstractControl
is the mothership of our form that other form controls simply extend/inherit from, which means we can actually talk to any form control in the group. If you recall from the previous article, we can fetch information about our form controls via .get(<control>)
to implement client-side validation errors, for example:Incidentally, we can also use this same API when implementing custom validators, so referencing our previous form group code, in which we have nested
FormGroup
props email
and confirm
, let’s go grab them:At this point,
control
is FormGroup
. Our email
and confirm
are both FormControl
, if we logged them out in the console
we’d see this:Custom validation properties
Now we’re ready to do some fun stuff! All we actually want to do is compare that both theemail
and confirm
fields have the same value, which will in turn display errors if they are invalid. Let’s check the .value
property (the actual FormControl
value, i.e. the <input>
) and if they match we’ll return null
(which internally sets the validation state for the entire group, and entire form where applicable):So until now, this means that if everything is working perfectly, we’ll return no errors. Now we need to add that custom validation.
Custom validation Object hook
What we want to implement is the validation that matches this HTML:Ignoring the HTML, we’re interested specifically in this piece:
This means that we want to be able to query the
account
level FormGroup
,
and check if it has an error called “nomatch”. To implement this we
require a custom Object to be returned from our validator should the
values not match:We can condense this nicely onto a one line ternary, final code:
Now, we import our validator, and add it to the second argument of the
account
level FormGroup
:
No comments:
Post a Comment