Instructor: We have here a very simple form which follows the template-driven forms approach in Angular, as you can see here by specifying this ngModel. Then, I have also added here some required validators, and at the bottom here already some handling of the errors that get shown whenever such required validator, for instance, fails.
You can see, I do that by basically accessing here the firstName template variable, which you can see up here. That basically binds the instance of the ngModel to that firstName such that I can then go and query here the invalid property.
I also concatenated that the value gets only shown whenever interact with my field. Basically, if one clicks in or modifies and clicks out again, you can see that the error message, which is specified below here basically on the required property, gets shown.
Now, what we would like to achieve here is to create our own custom validator. Let's take a look how we can do that. Let's call it name.validator.ts, and here basically we define a simple function which will be our name validator. That will return a validator function, which we can see gets imported here from the Angular forms.
Then this name validator is like a factory function. What it does is returns another function, which returns validation errors or null in case when everything goes fine, so when the validation succeeds. That validation function inside here takes a control, which is a so-called abstract control, which comes also from the Angular forms package.
Great. Now, inside here, we basically do the validation. We could say something like const is valid is whenever the control.value is equals to empty, or when the control.value matches some property which we define, so some fixed value which we define. Let's call it name.
Now, let's see. We have to pass that in. That comes in here at the very top of our validation function. Then below here, we could say whenever it is valid, we basically return null. This is how form validation works.
In our case, we can basically return an object. That object can be used by someone that uses our validation to display a proper message. Let's called it nameMatch, which will be the name of our validator.
Here inside there, we basically provide a property which says the allowed name that has been specified is basically name, which comes in from up here. Great, so our validator function is done. Now, if you take a look at our personAdded component, the validations on the template-driven forms in Angular get added as attributes on the field.
These attributes are basically implemented as directives. If you want to add here our nameMatch, we have to obviously create a directive ourselves as well. Let's call it name.validator.directive. Let's simply place in here a scaffold of a directive in Angular.
Now, we need to specify here our validator, so the selector, and we call it nameMatch. We want to also add here ngModel. Basically, we are telling that directive should only match whenever obviously our name match has been specified on some input field, but also if ngModel's present, because then we are in the context of a template-driven form.
The next step we implement here, the validator interface. That comes from the Angular form as well. The validator interface basically has a validate method, which takes a form control. Inside here, we can now implement the validation.
Now, obviously you know the validation has already been implemented here in our name validator, so let's import that from that name validator. We can now obviously reference it below here. We can say something like return name.validator.
We pass in some name that we allow the user to specify, so let's hard-code it for now to Yuri. Now, this will return our validation function, and now in that validation function, invoke it immediately, and pass in the form control.
Before going ahead, we need to import here the ngValidators inject token, and we need to then register it on the provider section here. We basically say provide ngValidators, and use here basically our directive. We also have to say multi true, because obviously it can be multiple constant validators registered on here.
Now, as with all directives and components in Angular as well as service, we need to go to the module and register it there. Let's import it, and also to reference it here below in the decoration section. Once that is done, we can jump back to our personAdded component. We can now start adding our nameMatch validator.
Obviously, we also have to handle possible error cases. We have display basically some validation message to our user. Similarly, as here with the required validator, we can now here add nameMatch, and this one here has to exactly match with the one we specified here at return type.
Below here, we can give some proper error message. Here the interesting part comes, because we basically can now access our object, which we return in our name validator. We can access here the allowedName property. We can say something like firstName.allowedName.
Now, whenever I type in here something, you can see that below, the error message comes out. When Yuri is in there, it works, otherwise, basically we get an exception. Now, obviously hard-coding here Yuri doesn't make a lot of sense.
We can easily customize that via input. We specify here a nameMatch input, and below here, we say like this.nameMatch. Now, we can go back, and here basically set that to tomorrow value, just Pete. I'll save this.
Now, if I write Yuri, it doesn't work, you can see. Below, it shows that we have to specify Pete. In the same way, obviously we can here directly bind this to some property which resides in our component behind, so valid name.
That valid name can be something we specify here on our component, which could also be done dynamic. You can see, now it don't accepts Jack.