Angular provides a certain number of ways to validate your input data. With attributes like required
or maxlength
we can easily control what a user can and cannot insert in a form.
However, what if the validation has to happen on the backend? How do I implement a custom validator that is able to call a backend service and return the validation result?
In this article, I will guide you through the steps you need to perform in order to create your custom asynchronous validator.
The backend service
For the purpose of this example, let's imagine we have an endpoint which accepts, in the request body, a structure like the following one:
{
"objectType": "SOME_OBJECT_TYPE",
"value": "Some name"
}
Where value
is the value to validate and objectType
contains some information needed by the sever.
To this call, the server responds with another object containing the field valid
(the validation result) and a message:
{
"valid": false,
"message": "Must not be blank"
}
The validator
To create a custom validator, we need to create a new directive by running:
ng generate directive nameIsValid
Let's define the directive decorator first:
@Directive({
selector: '[nameIsValid][ngModel],[nameIsValid][FormControl]',
providers: [
{
provide: NG_ASYNC_VALIDATORS,
useExisting: NameIsValidDirective,
multi: true
}
]
})
The code above registers the validator, so Angular is aware it exists and can use it during binding. This means that thereafter we can use it in an input field as in the following example:
<input
type="text"
id="name"
name="name"
nameIsValid
>
Moving back to the directive, we can now populate the validator class:
export class NameIsValidDirective implements AsyncValidator {
private validationServiceUrl = "my backend url";
constructor(
private http: HttpClient
) { }
}
As you can see, the NameIsValidDirective
class implements the AsyncValidator
interface which requires us to define a validate
function:
validate(control: AbstractControl):
Observable<ValidationErrors | null> {
let fieldToValidate = {
objectType: "NAME_VALUE",
value: control.value
};
const request = this.validationServiceUrl
const obs = this.http.post<any>(request, fieldToValidate)
.pipe(
map((validationResult) => {
// null no error, object for error
return validationResult.valid === true ? null : {
NameIsValidDirective : validationResult.message
};
})
);
return obs;
}
control.value
contains the value the user inserted in the input field. With this information, we build the object to send to the backend, make a call and get the response: if the validation went good, we must return null
, otherwise we must return an object. In this example, I chose to return the validation error message.
Conclusions
Despite typically there is no need to create custom validators at all, there are specific cases or architectural needs where we require to perform a validation on the backend side. When this case occurs, you are now aware of how custom validators work and you successfully learned how to implement one.
Top comments (0)