DrupalGap icon indicating copy to clipboard operation
DrupalGap copied to clipboard

How to prevent a user clicking the submit button twice

Open luxio opened this issue 9 years ago • 14 comments

Some of our users are impatient an click twice (or more) on the submit button when submitting a node, which results in creating multiple identical nodes.

Do you have any ideas, how to prevent (e.g. disabling the submit button) while posting to the server?

luxio avatar Sep 28 '16 14:09 luxio

@luxio This problem is becoming more common, and I'd like to build its solution directly into DG core.

In _drupalgap_form_submit(), if we do this after the form has been loaded:

https://github.com/signalpoint/DrupalGap/blob/7.x-1.x/src/includes/form.submission.inc.js

var submitButton = $('#' + form_id + ' button.dg_form_submit_button');
$(submitButton).prop('disabled', true);

That'll disable the button, then if the form fails to validate, we can re-enable it in _drupalgap_form_submit():

https://github.com/signalpoint/DrupalGap/blob/7.x-1.x/src/includes/form.submission.inc.js#L57

$(submitButton).prop('disabled', false);

Otherwise, I think we can just leave the submit button disabled forever, and let the developer re-enable it if they choose to, otherwise typically I recommend sticking a reloadPage option on any links to the form, that way next time the form is visited, it is loaded fresh.

Thoughts?

signalpoint avatar Sep 28 '16 14:09 signalpoint

Thanks. Looks good.

I think the button should be also reenabled, if the service call returns an error (server down, network connection problem) so that the user can try to submit it again, without having entering the data again.

luxio avatar Sep 28 '16 15:09 luxio

I think the button should be also reenabled, if the service call returns an error (server down, network connection problem) so that the user can try to submit it again, without having entering the data again.

Agreed. This will probably have to be on a per form basis, i.e. the user login form. We could let DG core automatically disable it, but then depending on what happens in the validation/submission handler of each form, they should be in charge of re-enabling it if necessary.

signalpoint avatar Sep 28 '16 15:09 signalpoint

Where could I put in the node_edit form? In the error closure of the submission handler?

luxio avatar Sep 28 '16 16:09 luxio

Need this as well

devasghar avatar Sep 28 '16 16:09 devasghar

I am trying to hack the core for this. Modified /src/includes/form.submission.inc.js Executed make shows

Generating aggregated drupalgap.js file. done

Updated index.html from

<script type="text/javascript" charset="utf-8" src="bin/drupalgap-7.0.2.min.js"></script>

To

<script type="text/javascript" charset="utf-8" src="bin/drupalgap.js"></script>

But no luck, what am i missing ???

Note : No errors on make even if i mess with the src/* files

devasghar avatar Sep 28 '16 17:09 devasghar

@devasghar Do you have npm installed? I'd recommend that instead, then you can just run grunt from the app's www directory, and it'll listen for changes to the src directory, and then autocompile the binaries for you. Either way though, the makefile should still work fine, I have it in case I'm developing on an environment that won't support npm.

signalpoint avatar Sep 28 '16 18:09 signalpoint

@luxio This would probably be the best spot(s):

  • https://github.com/signalpoint/DrupalGap/blob/7.x-1.x/src/modules/entity/entity.js#L606
  • https://github.com/signalpoint/DrupalGap/blob/7.x-1.x/src/modules/entity/entity.js#L617

That way it'll cover it for all core entity types.

signalpoint avatar Sep 28 '16 20:09 signalpoint

@signalpoint grunt worked fine,

I am sticking to the first solution for now, and its working https://github.com/signalpoint/DrupalGap/blob/7.x-1.x/src/includes/form.submission.inc.js

/**
 * Handles a drupalgap form's submit button click.
 * @param {String} form_id
 * @return {*}
 */
function _drupalgap_form_submit(form_id) {
  try {
    // Load the form from local storage.
    var form = drupalgap_form_local_storage_load(form_id);
+   // disabling the submit button to prevent multiple submission
+   var submitButton = $('#' + form_id + ' button.dg_form_submit_button');
+   $(submitButton).prop('disabled', true);
    if (!form) {
      var msg = '_drupalgap_form_submit - ' + t('failed to load form') + ': ' +
        form_id;
      drupalgap_alert(msg);
      return false;
    }

    // Assemble the form state values.
    var form_state = drupalgap_form_state_values_assemble(form);

    // Clear out previous form errors.
    drupalgap.form_errors = {};

    // Build the form validation wrapper function.
    var form_validation = function() {
      try {

        // Call the form's validate function(s), if any.
        for (var index in form.validate) {
            if (!form.validate.hasOwnProperty(index)) { continue; }
            var function_name = form.validate[index];
            var fn = window[function_name];
            fn.apply(null, Array.prototype.slice.call([form, form_state]));
        }

        // Call drupalgap form's api validate.
        _drupalgap_form_validate(form, form_state);

        // If there were validation errors, show the form errors and stop the
        // form submission. Otherwise submit the form.
        if (!jQuery.isEmptyObject(drupalgap.form_errors)) {
          var html = '';
          for (var name in drupalgap.form_errors) {
              if (!drupalgap.form_errors.hasOwnProperty(name)) { continue; }
              var message = drupalgap.form_errors[name];
              html += message + '\n\n';
          }
+       //enabling the submit button again
+       $(submitButton).prop('disabled', false);
          drupalgap_alert(html);
        }
        else { form_submission(); }
      }
      catch (error) {
        console.log('_drupalgap_form_submit - form_validation - ' + error);
      }
    };

However the button is not being enabled on node_edit, node_add, comment_edit, comment_add errors like this one POST localhost/site/?q=drupalgap/node.json 406 (Not Acceptable: An illegal choice has been detected. Please contact the site administrator.)

q=drupalgap/node.json 406 q=drupalgap/comment.json 406

Where to look ??

devasghar avatar Sep 29 '16 07:09 devasghar

@devasghar Thank you for sharing the feedback, I'm glad we are on the right track. Please see the two links mentioned above as the spots to handle the submission of all entity forms.

signalpoint avatar Sep 29 '16 13:09 signalpoint

Please review commit

This is working well so far

Note: button will still be disabled on javascript:drupalgap_back();

devasghar avatar Sep 29 '16 17:09 devasghar

@devasghar see my comment.

I also like the idea of using the technique directly in _drupalgap_form_submit(), that way ever single form will have this behavior, not just entity forms. We can use the entity form submission handlers to re-enable the button if there was a problem. Other forms may need to take special action to re-enable the button, e.g. user login form when an incorrect password is entered. We're on the right track here though, thank you.

signalpoint avatar Sep 29 '16 20:09 signalpoint

Ok now we have this on both _drupalgap_form_submit() & entity level & is working so far, but we need to handle the 401 now

POST localhost/site/?q=drupalgap/user/login.json 401 (Unauthorized: Wrong username or password.) POST localhost/site/?q=drupalgap/node.json 401 (Unauthorized: Access denied for user anonymous)

devasghar avatar Sep 30 '16 09:09 devasghar

@signalpoint

@luxio This would probably be the best spot(s):

https://github.com/signalpoint/DrupalGap/blob/7.x-1.x/src/modules/entity/entity.js#L606 https://github.com/signalpoint/DrupalGap/blob/7.x-1.x/src/modules/entity/entity.js#L617 That way it'll cover it for all core entity types.

Thanks, works great. I am also disabling the the submit button in drupalgap_entity_form_submit(), in order to prevent having the submit button still being disabled in other forms (e.g. user login) in case of an error.

luxio avatar Oct 04 '16 17:10 luxio