<script type="text/javascript">
$(document)
.ready(function() {
//Intercept Submit button in order to make ajax call instead of a postback
$('#contour_form_@FORMID').preventDoubleSubmission();
});
// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function () {
$(this).on('submit', function (e) {
e.preventDefault();
var $form = $(this);
if ($form.data('submitted') === true) {
// Previously submitted - don't submit again
} else {
if ($form.valid()) {
// Mark it so that the next submit can be ignored
$form.data('submitted', true);
// Make ajax call form submission
$.ajax({
url: $form.attr('action'),
type: 'POST',
cache: false,
data: $form.serialize(),
success: function (result) {
console.log(result);
var thankYouMessage = $(result).find('.contourMessageOnSubmit');
$('#contour_form_@FORMID').html(thankYouMessage);
}
});
}
}
});
// Keep chainability
return this;
};
</script>
2018 now and this solution came in handy. I reworked it slightly to suit the change of class names to "umbraco-forms" and to make it more generic (so this would go in a generic JS file).
$(document).ready(function() {
//Intercept Submit button in order to make ajax call instead of a postback
$('.umbraco-forms-form form').preventDoubleSubmission();
});
// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function () {
$(this).on('submit', function (e) {
e.preventDefault();
var $form = $(this);
if ($form.data('submitted') === true) {
// Previously submitted - don't submit again
} else {
if ($form.valid()) {
// Mark it so that the next submit can be ignored
$form.data('submitted', true);
// Make ajax call form submission
$.ajax({
url: $form.attr('action'),
type: 'POST',
cache: false,
data: $form.serialize(),
success: function (result) {
var thankYouMessage = $(result).find('.umbraco-forms-submitmessage').first();
$form.html(thankYouMessage);
}
});
}
}
});
// Keep chainability
return this;
};
Do you guys think it would be useful to turn this into a package or Forms theme? I remember the first time implementing this for contour years ago and it was a challenge to even find where to put these scripts.
Would be nice if it were more plug and play and could be updated in one place as the markup / scripts for Forms evolve.
I don't think a package would add much value with my tweaked solution above, as it's not a C# solution, but simple JQuery. This is generic and would just be thrown into a JS file (either in the head of end of should work).
That earlier Contour solution (where it included some Razor), would have needed to be included in a specific razor file to work.
ajax protocol can't detect redirects, the browser makes it invisible for it. This can be done with a new API called fetch which has around 85% users support according to canisue
Seems a bit of a can of worms. I don't think you'll be able to remove the "redirect to page" functionality from forms itself, so fundamentally you'd be breaking a core functionality of Umbraco Forms. Bad karma.
What I thought I could do in my case was to rely on something on the page so that I know that the response is a different URL (e.g. I've been redirected). Canonical meta tags are a good candidate. I could just do
Get result html
Get canonical meta value
Is it the same url ? (handle fragments, query strings and protocols here)
If different it was a redirect, redirect to that page.
This sounds a bit hacky and would avoid if not necessary
I've just tested this solution which as un-sexy as it is is dead simple and works with all Umbraco Forms functionality
USE CODE FROM MY NEXT ANSWER INSTEAD!
// Find a submit message that is not wrapped in a form (the message is always present even when not visible
// as the form has just been loaded.
let message = $('.umbraco-forms-submitmessage:not(form .umbraco-forms-submitmessage)').first();
if(message)
{
// Scroll page to the submit message. Here 20 is just to add some margin, but you could need to add
// the height of your sticky navbar too.
let targetOffset = message.offset().top - 20;
$('html,body').animate({scrollTop: targetOffset});
}
This however doesn't scroll down to the 2nd+ step of forms, so I'll have a look at that now
I'll one up myself by supporting multi pages forms, and I've opened a ticket in regards to previous pages hidden field not being populated:
$(document).ready(function () {
function scrollToFormIfInteractedWith() {
// Find a target to scroll to if the user is interacting with forms. This happens in different ways:
// - Find a submit message that is not wrapped in a form (the message is always present even when not visible
// as the form has just been loaded.
// - If the message wasn't found then try to target a form containing a hidden field that indicates that the form's
// step/page is not the first(0) one.
// - If that wasn't found either then try to find a form that has PreviousClicked = true where the user just
// navigated back. This is CURRENTLY NOT WORKING possibly because of an Umbraco Forms issue for which I've raise
// a github issue here https://github.com/umbraco/Umbraco-CMS/issues/4443.
let target = $('.umbraco-forms-submitmessage:not(form .umbraco-forms-submitmessage)')[0]
|| $('input[type="hidden"][name="FormStep"]:not([value="0"]),'
+ 'input[type="hidden"][name="PreviousClicked"][value="true"]').parent('form')[0];
// If we have found a target to scroll to then smooth scroll there.
if (target) {
// Scroll page to the target. Here 20 is just to add some margin, but you could need to add
// the height of your sticky navbar too.
let targetOffset = $(target).offset().top - 20;
$('html,body').animate({scrollTop: targetOffset});
}
}
scrollToFormIfInteractedWith();
});
Just to expand on the AJAX approach, which still doesn't address the issue with multi step forms & redirections, but thought I should document my tweaks here as this will come up for others also.
Our original AJAX solution doesn't work with posting files to the server and with handling Recaptcha errors. A long way from perfect, but my basic code tweak is below:
$(document).ready(function() {
//Intercept Submit button in order to make ajax call instead of a postback
$('.umbraco-forms-form form').preventDoubleSubmission();
});
// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function () {
$(this).on('submit', function (e) {
e.preventDefault();
var $form = $(this);
if ($form.data('submitted') === true) {
// Previously submitted - don't submit again
} else {
if ($form.valid()) {
// Mark it so that the next submit can be ignored
$form.data('submitted', true);
// Make ajax call form submission
$.ajax({
url: $form.attr('action'),
type: 'POST',
cache: false,
data: new FormData(this),
processData: false,
contentType: false,
success: function (result) {
var thankYouMessage = $(result).find('.umbraco-forms-submitmessage').first();
//Handles edge case where Recaptcha wasn't checked
if (thankYouMessage.length == 0) {
$(result).find('.field-validation-error').each(function (i, v) {
window.alert($(v).text());
});
$form.data('submitted', false);
}
else {
$form.html(thankYouMessage);
}
}
});
}
}
});
// Keep chainability
return this;
};
Has anyone tried this solution with the latest version of forms? I'm trying this with 8.6 and the page still reloads on submit. The return message shows first and then the page reloads almost as if it's ignoring preventDefault? I've tried to figure this out all day and have had no lucky. Any suggestions?
I got this working on a single stepped form with Umbraco forms 8.7 using the below jquery:
<script type="text/javascript">
$(document)
.ready(function() {
//Intercept Submit button in order to make ajax call instead of a postback
$('#umbraco_form_@(Guid.Parse((string) Model.YOURFORMID).ToString("N")) form').preventDoubleSubmission();
});
// jQuery plugin to prevent double submission of forms
jQuery.fn.preventDoubleSubmission = function () {
$(this).on('submit', function (e) {
e.preventDefault();
var $form = $(this);
if ($form.data('submitted') === true) {
// Previously submitted - don't submit again
} else {
if ($form.valid()) {
// Mark it so that the next submit can be ignored
$form.data('submitted', true);
// Make ajax call form submission
$.ajax({
url: $form.attr('action'),
type: 'POST',
cache: false,
data: $form.serialize(),
success: function (result) {
console.log(result);
var thankYouMessage = $(result).find('.umbraco-forms-submitmessage');
$('#umbraco_form_@(Guid.Parse((string) Model.YOURFORMID).ToString("N"))').html(thankYouMessage);
}
});
}
}
});
// Keep chainability
return this;
};
</script>
I was able to get a multi step form to submit successfully via ajax with the following code.
angular.module(module || 'app')
.controller('UmbracoFormsController', UmbracoFormsController);
UmbracoFormsController.$inject = ['$rootScope', '$scope', '$element', '$attrs', '$http', '$compile'];
function UmbracoFormsController($rootScope, $scope, $element, $attrs, $http, $compile) {
var ctrl = this;
ctrl.$onInit = function () {
};
ctrl.goBack = () => {
ctrl._goBack = true;
}
ctrl.submitForm = function ($event, isMultiStep) {
$event.preventDefault();
if (!ctrl.submitting) {
ctrl.submitting = true;
postForm(isMultiStep);
}
};
function postForm(isMultiStep) {
var valid = false;
if (!ctrl._goBack) {
$element.validate();
valid = $element.valid();
if (!valid) {
ctrl.submitting = false;
}
}
if (ctrl._goBack || valid) {
var data = {};
var values = $element.serializeArray();
values.forEach(function (item, index) {
if (data[item.name] === undefined) { // New
data[item.name] = item.value || '';
} else { // Existing
if (!data[item.name].push) {
data[item.name] = [data[item.name]];
}
data[item.name].push(item.value || '');
}
});
if (ctrl._goBack) {
data['__prev'] = $element[0].querySelector('[name="__prev"]').innerText;
}
$.post({
method: 'POST',
url: location.pathname,
data: data,
success: function (response) {
var html = document.createElement('html');
html.innerHTML = response;
var container = $element.parent();
var id = container.attr('id').replace('umbraco_form_', '');
var form = html.querySelector('#' + $element.parent().attr('id'));
if (!form) {
var popup = $('#form_popup_' + id);
popup.foundation('open');
var pageForms = Array.from(document.querySelectorAll('#umbraco_form_' + id + ' form'));
pageForms.forEach(pageForm => {
pageForm.reset();
});
}
else if (isMultiStep) {
$element.html(form.innerHTML);
$compile($element.contents())($scope);
$element.removeData("validator").removeData("unobtrusiveValidation");
$.validator.unobtrusive.parse($element);
}
$scope.$evalAsync(function () {
ctrl.submitted = true;
ctrl.submitting = false;
});
},
error: function () {
$scope.$evalAsync(function () {
ctrl.submitting = false;
});
}
});
}
ctrl._goBack = false;
}
}
My solution uses angularjs, but the same technique would work for any javascript framework. It involves the following.
Determine if the "previous" button triggered the submission and if it did, add a value for the name "__prev" to the data that will be posted (the code does this by setting the "goBack" flag to true when that button is clicked and then checking that flag). This will send the form back to the previous step. If it's left out, the form moves on to the next step.
Post the form data at the current url (same as what's done for single step forms).
Find the form in the response html.
If no form is found in the response html, the form has been fully submitted and a modal with a thank you message is displayed (I'm using foundation reveals for the message, so the foundation call is used to reveal it). Otherwise it moves on to step 5.
Replace the content in the form with the form content from the response.
Apply any scripts that need to be run against the newly injected content (this is what the $compile clause is doing in my code).
Reinitialize the unobtrusive validation, as it won't automatically pick up on the new inputs (lines with removeData("validator") and $.validator.unobtrusive.parse($element)).
One point that might be worth knowing - if using reCAPTCHA-3 and AJAX, you may need to reload the reCAPTCHA-3 client in the Success callback of the Post, otherwise it will likely exit to a missing reCAPTCHA-3 client and throw an error along the lines of: "Error: No reCAPTCHA clients exist".
I solved this for my use by adding a function to the reCAPTCHA-3 field (so I could easily init the keys, form-id, etc) and calling that from the $.Post Success callback, but I'm sure there are more elegant ways to handle it.
@MB You were right. Out of the box, the recaptcha 3 implementation initializes after the "DOMContentLoaded" event fires. Since the recaptcha loads after this step in an ajax multi step form, I tweaked the script to check whether the dom has already initialized and initialize recaptcha if it has. I changed it from
document.addEventListener('DOMContentLoaded', function () {
// Disable the submit button for this form, until we actually have a key from Google reCAPTCHA
hiddenField.form.querySelector('[type=submit]').setAttribute('disabled','disabled');
window.grecaptcha.ready(function () {
timerFunction();
});
});
"loadScript" is just a helper method I created to load scripts via javascript and then run the optional callback function after that script has loaded. Note that the loadScript wrapper may not be required. For some reason
didn't load the script in my build, so I loaded it with that utility method. Suspect it's because of all the changes I've made to the form views though.
Ajax Umbraco Forms
Hi Community,
What is the best way of doing Ajax Umbraco Forms?
Is it possible out of the box in latest version?
Thanks,
Alex
See the method posted by Thomas here, it works with some simple updates to the main view for the form: https://our.umbraco.org/forum/umbraco-pro/contour/60788-Umbraco-Forms-and-Ajax#comment-233528
Thanks, Amir, nice solution, will try.
Maybe something easier?
Thanks
Solved! This code works for me:
Thanks,
Alex
2018 now and this solution came in handy. I reworked it slightly to suit the change of class names to "umbraco-forms" and to make it more generic (so this would go in a generic JS file).
Hi Danny
Thank you so much for sharing!!!
Alex
Do you guys think it would be useful to turn this into a package or Forms theme? I remember the first time implementing this for contour years ago and it was a challenge to even find where to put these scripts.
Would be nice if it were more plug and play and could be updated in one place as the markup / scripts for Forms evolve.
I'd be happy to do it if so.
-Amir
I don't think a package would add much value with my tweaked solution above, as it's not a C# solution, but simple JQuery. This is generic and would just be thrown into a JS file (either in the head of end of should work).
That earlier Contour solution (where it included some Razor), would have needed to be included in a specific razor file to work.
I don't think this will work with multi-steps forms and be compatible with thank you pages redirection tho!
Hi Stefano, you are right. It's not a perfect solution.
Spent a few hours on this and here are my findings:
I've just tested this solution which as un-sexy as it is is dead simple and works with all Umbraco Forms functionality
USE CODE FROM MY NEXT ANSWER INSTEAD!
This however doesn't scroll down to the 2nd+ step of forms, so I'll have a look at that now
awesome finding Stefano, didn't try umbraco-ajax-form but it looks interesting
I'll one up myself by supporting multi pages forms, and I've opened a ticket in regards to previous pages hidden field not being populated:
Just to expand on the AJAX approach, which still doesn't address the issue with multi step forms & redirections, but thought I should document my tweaks here as this will come up for others also.
Our original AJAX solution doesn't work with posting files to the server and with handling Recaptcha errors. A long way from perfect, but my basic code tweak is below:
Has anyone tried this solution with the latest version of forms? I'm trying this with 8.6 and the page still reloads on submit. The return message shows first and then the page reloads almost as if it's ignoring preventDefault? I've tried to figure this out all day and have had no lucky. Any suggestions?
any luck?
Hi Alex Just wondering do u get a solution that works for this? Am struggling to implement. When I implement it still jumps to top of page
I got this working on a single stepped form with Umbraco forms 8.7 using the below jquery:
I was able to get a multi step form to submit successfully via ajax with the following code.
My solution uses angularjs, but the same technique would work for any javascript framework. It involves the following.
One point that might be worth knowing - if using reCAPTCHA-3 and AJAX, you may need to reload the reCAPTCHA-3 client in the Success callback of the Post, otherwise it will likely exit to a missing reCAPTCHA-3 client and throw an error along the lines of: "Error: No reCAPTCHA clients exist".
I solved this for my use by adding a function to the reCAPTCHA-3 field (so I could easily init the keys, form-id, etc) and calling that from the $.Post Success callback, but I'm sure there are more elegant ways to handle it.
I need to test my integration with a recaptcha field, but you're probably right on that. Thanks for pointing it out.
@MB You were right. Out of the box, the recaptcha 3 implementation initializes after the "DOMContentLoaded" event fires. Since the recaptcha loads after this step in an ajax multi step form, I tweaked the script to check whether the dom has already initialized and initialize recaptcha if it has. I changed it from
to
"loadScript" is just a helper method I created to load scripts via javascript and then run the optional callback function after that script has loaded. Note that the loadScript wrapper may not be required. For some reason
didn't load the script in my build, so I loaded it with that utility method. Suspect it's because of all the changes I've made to the form views though.
is working on a reply...