Last week I came across an article that talks about how we (probably) don’t need ReCAPTCHA. Although I have already written a detailed article about how to integrate ReCAPTCHA in Angular, inspired by this article, I decided to create a math Captcha component, that implements a custom validation and share the result with you.

Let’s get started.

Create the Captcha Component

We want our captcha component to integrate with Angular forms, so first, let’s implement the ControlValueAccessor interface:

We have implemented the three methods that the ControlValueAccessor requires. In our case, we don’t care about the writeValue() method, so we leave it empty. If you want to learn more about this topic, check out the following article:

Next, let’s create the component’s template. In order to display the question, we’ll use canvas. This will make sure it will not be present in the DOM, thus avoiding robots from grabbing it.

Now, let’s grab a reference to the canvas element from the component and draw the question:

The createCaptcha() method is straightforward. It generates two random numbers, and draws them by using the fillText canvas method. We also save the question’s answer, so that we can validate that against the user’s input.

Let’s see how we can implement the validation part. There are many ways to add custom validation to a form control, but in our case, we want the validation to be baked into in the component.

Using the NG_VALIDATORS Provider

We need to register our component with the NG_VALIDATORS provider, and implement the validate() method:

The validate() method will be called with the FormControl instance when the control’s value changes. When it’s called, we check the current control value against the question’s answer. If it doesn’t match, we return an error object, which will mark the current control as invalid.

Now, we’re ready to use our captcha component:

We can export the ngForm directive to a local variable and use its submitted property as an indication to whether the form submission has been triggered. If it has been, and the captcha is invalid, we show the error.

Manually Register the Control

The alternative is to obtain a reference to the form control directive via DI, and set the value accessor property manually:

We have a reference to the underlying control, and we register the validator function using the setValidators method.

Note that when utilizing this option, we can’t register our component with the NG_VALUE_ACCESSOR provider; If we do so, we’ll have a circular dependency and an error will be thrown.

🚀 Have You Tried Akita Yet?

One of the leading state management libraries, Akita has been used in countless production environments. It is constantly being developed and improved.

Whether it is entities arriving from the server or UI state data, Akita has custom-built stores, powerful tools, and tailor-made plugins, which all help to manage the data and negate the need for massive amounts of boilerplate code. We/I highly recommend that you try it out.

Follow me on Medium or Twitter to read more about Angular, Akita and JS!

You can play with the source code here.