9

I have a Sitecore 8.1 WFFM form in an MVC form rendering, with a save action that takes a few seconds to complete. After submitting the form, the user just sees the form page they submitted with no message to show that it is processing, until the server finishes the save actions and redirects to the success page or shows the success message. This seems to be the same regardless of whether the Is Ajax Mvc Form checkbox is selected or not for the WFFM form.

During this wait the user might try resubmitting the form again, which I need to prevent.

Example code for a slow save action is:

    using System;
    using System.Configuration;
    using Sitecore.Data;
    using Sitecore.WFFM.Abstractions.Actions;
    using Sitecore.WFFM.Actions.Base;

    namespace Sitecore.Custom.Forms.SaveActions
    {
        public class SlowSaveAction : WffmSaveAction
        {
            public override void Execute(ID formId, AdaptedResultList adaptedFields, ActionCallContext actionCallContext = null, params object[] data)
            {
                System.Threading.Thread.Sleep(8000);
            }
        }
    }

I have tried editing the default FormViewModel.cshtml with javascript to disable the submit button after the form is submitted (which works in pre-mvc versions of WFFM), but because the mvc form is dynamically reloaded after clicking submit, the button is immediately reset to enabled.

I have also looked in the Sitecore.Forms.Mvc.ViewModels.FormViewModel model for any value that might identify that the form is submitted and validation passed, but couldn't find anything.

Is there some way to disable the submit button whilst the form is processing? Or perhaps as an alternative, hide the button and show a 'processing' message?

5

I think it is still better to do via Javascript. You should be able to disable(hide button) on submit.

mvc form is dynamically reloaded after clicking submit, the button is immediately reset to enabled

I was not able to find JS code (wffm.js) that immediately reset button to enabled. Form is dynamically changed only on formSubmitError and formSubmitSuccess

$scw('#' + targetId).html(res);
$scw('#' + targetId).html(xhr.responseText);

It means that button could be enabled only when you receive response from server(form actions either succeed or failed).

But there is present code

e.preventDefault()

that could block execution of your JS code that makes button disabled. If my assumption is right you should force your code to be executed before WFFM submit event.

As a last resort WFFM scripts are not uglified and minimized, so you can easily modify them. Scripts are located here: sitecore modules\Web\Web Forms for Marketers\mvc\wffm.js. But you should remember about your modification when you will update WFFM module.

| improve this answer | |
2

For future developers, the following was my attempt at this by directly editing the file Anton mentioned (sitecore modules\Web\Web Forms for Marketers\mvc\wffm.js). Please do note this change in a shared wiki so future WFFM upgrades are also 'fixed' in the same way.

Complete excerpt:

$scw(form).submit(function (e) {
          $scw("input[type=submit]").attr("disabled", "disabled");      
          if ($scw(this).valid()) {

            var headersList = {
              "X-RequestVerificationToken": $scw(form).find('[name=__RequestVerificationToken]').val(),
              "X-Requested-With": "XMLHttpRequest"
            };

            if (notExternalSite) {
              $scw.ajax({
                url: this.action,
                type: this.method,
                processData: false,
                contentType: false,
                headers: headersList,
                data: new FormData(form.get(0)),
                success: function (res) {
                  self.formSubmitSuccess(form, res);
                  $scw("input[type=submit]").removeAttr("disabled");         
                },
                error: function (xhr, status, exception) {
                  self.formSubmitError(form, xhr, status, exception);
                  $scw("input[type=submit]").removeAttr("disabled");     
                }
              });

            } else {

              var formIdInput = form.find("input[id=" + form.attr("id") + "_Id]");
              var formItemIdInput = form.find("input[id=" + form.attr("id") + "_FormItemId]");
              var hostUrl = getFxmHost() + "/form/process?" + formIdInput.attr("name") + "=" + formIdInput.attr("value") + "&"
                  + formItemIdInput.attr("name") + "=" + formItemIdInput.attr("value");
              $scw.ajax({
                url: hostUrl,
                type: 'POST',
                dataType: "json",
                processData: false,
                contentType: false,
                data: new FormData(form.get(0)),
                success: function (res) {
                  self.formSubmitSuccess(form, res);
                  $scw("input[type=submit]").removeAttr("disabled");     
                },
                error: function (xhr, status, exception) {
                  self.formSubmitError(form, xhr, status, exception);
                  $scw("input[type=submit]").removeAttr("disabled");     
                },
                xhrFields: {
                  withCredentials: true
                }
              });

            }
            e.preventDefault();
          }
          else {
            $scw("input[type=submit]").removeAttr("disabled");     
          }
        });

Notice the addition and removal of the disabled attribute on submit buttons.

$scw("input[type=submit]").attr("disabled", "disabled"); $scw("input[type=submit]").removeAttr("disabled");

The else at the end was my own additional to ensure it was removed on a form validation failure.

else {
    $scw("input[type=submit]").removeAttr("disabled");     
}

I'd argue this could be done better by targeting the specific submit button in the form instead of all submit buttons, but this solution worked for my use case. I encourage others to either edit mine to target that forms submit button or create a new answer.

| improve this answer | |
  • You can add the complete function in the ajax options instead of using success and error. This way you're sure in all cases, the button will be re-enabled. Also the button selector can be changed to var submitBtn = $scw(form).find("input[type=submit]"); then use submitBtn.attr("disabled", "disabled"); right after a valid form and submitBtn.removeAttr("disabled"); – Tim Vermaelen Feb 6 at 17:30
0

A slightly changed answer based on @damanptyltd 's answer.

This improves the following:

  • buttons are only disabled once the form is valid
  • in any case, the buttons will be re-enabled
  • a little bit more performant in selecting the right button for the form

WFFM.js

$scw(form).submit(function (e) {
    var submitBtn = $scw(form).find("input[type=submit]");
    var elementsToRemove = $scw(".survey-remove-before-post-dropdown:hidden"); 

    elementsToRemove.remove();
    elementsToRemove = $scw(".survey-remove-before-post-radio:hidden"); 
    elementsToRemove.remove();

    if ($scw(this).valid()) {
        // disable submit for large requests, if not the user can submit multiple times
        submitBtn.attr("disabled", "disabled");

        var headersList = {
            "X-RequestVerificationToken": $scw(form).find('[name=__RequestVerificationToken]').val(),
            "X-Requested-With": "XMLHttpRequest"
        };

        var toIgnore = $scw(".ignore");

        for (var i = 0; i < toIgnore.length; i++) {
            var x = $scw(toIgnore[i]);
            var attr = x.attr("data-val-required");
            var range = x.attr("data-val-range-min");

            if (typeof attr !== typeof undefined && attr !== false) {
                var controlType = x[0].type;
                var classNames = x[0].className;
                //console.log(controlType);
                if (controlType === "text" || controlType === "textarea") {
                    if (classNames.indexOf("hasDatepicker") !== -1) {
                        x.val("01-01-01");
                    } else if (range) {
                        x.val(range);
                    } else {
                        x.val("-");
                    }
                }
                if (controlType === "radio") {
                    var idElements = x[0].id.split("}-");
                    var aValue = idElements[1];
                    x.val(aValue);
                }
                if (controlType === "email") {
                    x.val("onbepaald@onbepaald.be");
                }
                if (controlType === "password") {
                    x.val("onbepaald");
                }
                if (controlType === "hidden" && classNames.indexOf("wfmDatebox") !== -1) {   // DATUM....
                    x.val("1900-01-01");
                }
                if (controlType === "checkbox") {
                    x.val("false");
                }
                if (controlType === "tel") {
                    x.val("000000000");
                }
                if (controlType === "select-one") {
                    x.selectedIndex = 0;
                }

                // can't set a value programmatically due to security reasons
                // file input cannot be required for this reason
                if (controlType === "file") {
                    $scw(x).rules('remove');
                }
            }
        }

        if (notExternalSite) {
            $scw.ajax({
                url: this.action,
                type: this.method,
                processData: false,
                contentType: false,
                headers: headersList,
                data: new FormData(form.get(0)),
                success: function (res) {
                    self.formSubmitSuccess(form, res);
                },
                error: function (xhr, status, exception) {
                    self.formSubmitError(form, xhr, status, exception);
                },
                complete: function() {
                    submitBtn.removeAttr("disabled");
                }
            });
        } else {
            var formIdInput = form.find("input[id=" + form.attr("id") + "_Id]");
            var formItemIdInput = form.find("input[id=" + form.attr("id") + "_FormItemId]");
            var hostUrl = getFxmHost() + "/form/process?" + formIdInput.attr("name") + "=" + formIdInput.attr("value") + "&" + formItemIdInput.attr("name") + "=" + formItemIdInput.attr("value");
            var dataX = new FormData(form.get(0));

            $scw.ajax({
                url: hostUrl,
                type: 'POST',
                dataType: "json",
                processData: false,
                contentType: false,
                data: dataX,
                success: function (res) {
                    self.formSubmitSuccess(form, res);
                },
                error: function (xhr, status, exception) {
                    self.formSubmitError(form, xhr, status, exception);
                },
                complete: function() {
                    submitBtn.removeAttr("disabled");
                },
                xhrFields: {
                    withCredentials: true
                }
            });

        }
        e.preventDefault();
    }
});
| improve this answer | |

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.