Hey again,
apparently there is a timing-issue for this which causes the code to (sometimes) prevent the form from sending and after that "unlock" the button. Meaning: you will need to click the submit-button twice. We missed this because people just clicked the button twice because they thought they just didn't click it correctly.
Either way, I would be happy if someone would try this out with the recaptchav3 hook. For now, we fixed the behavior above by sending all of the information via an ajax call, which restricts it to javascript operation and avoids any posts and redirects to other pages. So the timing problems go away when you setup a resource as a target to send to and make FormIt use ajax to post.
Just to clarify: The FormIt snippet is just the "receiver" for the action. So here's what you do to avoid this:
#1: Create a resource as a FormIt Ajax Responder
#2: Assign a template to it with the FormIt call (make sure it responds JSON, see documentation here
https://docs.modx.com/extras/revo/formit and an example here:
https://sepiariver.com/modx/modx-forms-via-ajax/)
#3: Make the form post to that target and do the checking with the according libs inside the Javascript:
NOTE: THIS MIGHT ALL NOT BE NECESSARY WITH V3!
(function($){
$.notifyDefaults({
z_index: 100000,
type: 'success',
allow_dismiss: false,
delay: 5000,
newest_on_top: true,
offset: {
x: 50,
y: 100
}
});
var submitting = false;
var captureCompleted = false;
onError = function() {
console.log('captcha failed.');
if ( submitting ) {
$.notify({
title: "[[+errorTitle]]",
message: "[[+errorMsg]]",
icon: 'fa fa-warning'},{
type: "danger"
});
}
submitting = false;
var $this = $("#[[+formid]]"); // hard-code if you don't want to build that.
$this.find(".form-group").removeClass("has-error").removeClass("has-success");
$this.find('i.fa').removeClass('fa-cog fa-spin');
$this.find('button').removeClass("disabled");
};
onCompleted = function() {
console.log('captcha completed.');
captureCompleted = true;
if ( !captureCompleted ) {
console.log('captcha failed.');
submitting = false;
return false;
}
var $this = $("#[[+formid]]"), formData = $("#[[+formid]]").serializeArray();
/* reset posibble form errors the form when submitting */
$this.find(".form-group").removeClass("has-error").addClass("has-success");
$this.find("span").remove();
$.ajax({
type: 'POST',
url: $this.attr('action'),
dataType: 'json',
data: formData,
success: function(response) {
if (typeof gtag_report_conversion === "function") {
gtag_report_conversion();
}
$this.find('i.fa').removeClass('fa-cog fa-spin');
$this.find('button').removeClass("disabled");
if (response.success === true) {
$.notify({
message: response.message,
icon: 'fa fa-check'
});
/* reset the form */
$($this)[0].reset();
$this.find(".form-group").removeClass("has-error").removeClass("has-success");
$this.find(":input").removeClass("valid");
}
else {
$.notify({
title: "[[+errorTitle]]",
message: "[[+errorMsg]]",
icon: 'fa fa-warning'},{
type: "danger"
});
$.each( response.errors, function( index, value ) {
$("#" + index).parent().addClass("has-error").removeClass("has-success");
$('<span id="'+index+'-error" class="has-error help-block">'+value+'</span>').insertAfter("#"+index);
});
console.log("FormIt validation error");
submitting = false;
}
},
error: function(response) {
$this.find('i.fa').removeClass('fa-cog fa-spin');
$this.find('button').removeClass("disabled");
$.notify({
title: "[[+errorTitle]]",
message: "[[+serverErrorMsg]]",
icon: 'fa fa-warning'},{
type: "danger"
});
$this.find(".form-group").removeClass("has-error").removeClass("has-success");
console.log("FormIt Ajax Error");
submitting = false;
}
});
};
$(document).ready(function(){
/* only if we have a contact form on page */
if($("#[[+formid]]").length>0) {
/* validate the form - triggers jquery plugin */
var validator = $("#[[+formid]]").validate({
errorClass: "has-error help-block",
errorPlacement: function(error, element) {
error.insertAfter( element );
},
errorElement: "span",
highlight: function (element) {
$(element).removeClass("valid");
$(element).parent().removeClass("has-success").addClass("has-error");
},
success: function (element) {
$(element).addClass("valid");
$(element).parent().removeClass("has-error").addClass("has-success");
}
});
/* real submit function */
$(document).on("submit", "#[[+formid]]", function (event) {
event.preventDefault();
var $this=$(this);
/* Do not trigger if the user already triggered the form */
if (submitting === true) return false;
submitting = true;
console.log("recaptcha initialized");
$this.find('i.fa').addClass('fa-cog fa-spin');
$this.find('button').addClass("disabled");
/* Trigger google recaptcha. If this returns true either the callback "onCompleted" or "onError" will be executed */
grecaptcha.reset();
grecaptcha.execute();
});
}
});
})(this.jQuery);
Don't forget to add all the libs (recaptcha, gtm, etc. etc.). In our case there's also Bootstrap notify for the promises and stuff, you will need to add those or fix the code above to match your scenario.
Andreas