Using reCAPTCHA in Single-page applications

Recently I was writing my first Single-page application using Angular.js. The application should allow posting content as an anonymous user, so some sort of verification needs to be in place to make sure it’s actually a human user. For years, reCAPTCHA has been probably the most popular way to achieve that, so I went and used AngularJS reCaptcha component in my app. The examples of usage are quite simple:

<div vc-recaptcha theme="light" key="[my public key]"></div>

In my controller I called getResponse method to get the response value returned by reCAPTCHA, which is then sent to the server and validated there:

var response = vcRecaptchaService.getResponse();

I got it working within 15 minutes or so. It was great until I started noticing a strange error in my browser. After successful verification by reCAPTCHA, when submitting my form I got an error (I took the message from Chrome console):

TypeError: Cannot read property 'value' of null
    at Object.In [as getResponse] (https://www.gstatic.com/recaptcha/api2/r20150804120649/recaptcha__en.js:312:279)
    at Object.getResponse (http://localhost:61907/Scripts/angular-recaptcha.js:96:34)
    at n.$scope.submit (http://localhost:61907/App/controllers/viewController.js:85:49)
    at fn (eval at  (http://localhost:61907/Scripts/angular.min.js:1:0), :4:209)
    at f (http://localhost:61907/Scripts/angular.min.js:252:74)
    at n.$eval (http://localhost:61907/Scripts/angular.min.js:134:493)
    at n.$apply (http://localhost:61907/Scripts/angular.min.js:135:217)
    at HTMLButtonElement. (http://localhost:61907/Scripts/angular.min.js:252:126)
    at HTMLButtonElement.Hf.c (http://localhost:61907/Scripts/angular.min.js:35:217)

Not quite sure what’s happening, I kept ignoring this bug until other features were complete. Then I had to come back and fix this.

The reCAPTCHA JS file was minified, so it didn’t mean much when I took a look at the original source of the error. Furthermore, it all seemed to work when I tried to reproduce it. After some manual monkey testing to reproduce the bug, I took a look at the DOM structure generated by reCAPTCHA component. Bingo! the ID of the element where the response value from reCAPTCHA service is stored was changed: instead of standard “g-recaptcha-response”, it always was “g-recaptcha-response-1” when the error occurred. So the “Cannot read property ‘value’ of null” message in this case indicated that the component cannot find an element by ID…

That’s when it started to make sense to me. In my application, I had two forms with reCAPTCHA and they were in different views. Since Single-page applications do not reload the page, when rendering the second view, reCAPTCHA response element was automatically assigned a new unique ID by simply adding an incremented number to “g-recaptcha-response”.

Fixing the error was pretty straightforward then. getResponse method from reCAPTCHA accepts a widgetId parameter, which is the ID of reCAPTCHA response element. The fix in my code looks like the following:

<div vc-recaptcha theme="light" key="[my public key]" on-create="setRecaptchaId(widgetId)"></div>
$scope.setRecaptchaId = function(widgetId) {
    $scope.recaptchaId = widgetId;
};
...
var response = vcRecaptchaService.getResponse($scope.recaptchaId);

This code just executes a callback function to get and save the currently rendered reCAPTCHA reponse element ID. When the response is needed, this ID is then passed to getResponse method.

The exact source code may look different depending on the components that you use, but the general idea is the same: if you render reCAPTCHA more than once, you must pass a widgetId parameter when calling getResponse method.


7 thoughts on “Using reCAPTCHA in Single-page applications

  1. If you use the recaptcha lib of Google, you can also try this :

    render explicitly and :

    var grecaptchaID;
    grecaptchaID = grecaptcha.render(‘yourHTMLElem’, {sitekey: ‘PublicKEY’});

    and then : grecaptcha.getResponse(grecaptchaID);

Leave a comment