You are viewing a plain text version of this content. The canonical link for it is here.
Posted to users@tapestry.apache.org by Nathan Quirynen <na...@pensionarchitects.be> on 2023/02/24 13:11:20 UTC

Preventing double click eventlink and form submit

Hi,

I am looking for a solution to prevent multiple clicks on event links or 
form submits in our Tapestry application and in the meanwhile if 
possible giving the user visual feedback while the request is being 
processed.
We use ajax (async="true") on both our eventlinks and form components, 
but there are no javascript events to hook into right before the request 
is fired and when the request response is handled. But after some 
research I found I can use the zone parameter instead of the async 
parameter and then make use of the t5:zone:refresh and 
t5:zone:did-update events to do exactly that.
So I came up with following javascript module which can be used for both 
form submit buttons and eventlinks:

             var init = function(triggerClientId, zoneClientId) {

                 var trigger = $('#' + triggerClientId);
                 var zone = $('#' + zoneClientId);

                 var event_request = 't5:zone:refresh' + '.' + 
triggerClientId;

                 zone.on(event_request, function(e) {

                    // disable trigger + add css class
                     trigger.css('pointer-events', 'none');
                     trigger.addClass('request-in-progress');

                     var event_response = 't5:zone:did-update' + '.' + 
triggerClientId;
                     zone.on(event_response, function() {

                         // remove response event handler
                         zone.off(event_response);

                         // reenable trigger + remove css class
                         trigger.css('pointer-events', 'auto');
trigger.removeClass('request-in-progress');

                     });
                 });

             }

I would make new components AjaxForm/AjaxEventLink to wrap the original 
ones and implement everything there.
This seems to work as I hoped it would be, but then a zone element is 
added for every EventLink/Form which seems a bit like overkill, but I 
don't see another way at the moment.
I was wondering if others have tried to do something like I am, or if 
there's any better way of achieving what I am trying here? Or anything 
that I am missing could be wrong with my implementation?


Nathan


---------------------------------------------------------------------
To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
For additional commands, e-mail: users-help@tapestry.apache.org


Re: Preventing double click eventlink and form submit

Posted by Niran Abeygunawardena <Ni...@Clarivate.com.INVALID>.
Nice solution Ben 😊

We found that we couldn’t visual style a submit input button to give feedback that the request was being processed so instead created a mixin that when the submit button was clicked, it was converted into a link that could then be styled with a spinner and visually disabled. It is a very hacky solution so happy to see alternative solutions especially with a submit input button.


DisableAfterSubmit.java

import org.apache.tapestry5.ClientElement;
import org.apache.tapestry5.annotations.Environmental;
import org.apache.tapestry5.annotations.InjectContainer;
import org.apache.tapestry5.annotations.MixinAfter;
import org.apache.tapestry5.json.JSONObject;
import org.apache.tapestry5.services.javascript.JavaScriptSupport;

@MixinAfter
public class DisableAfterSubmit
{

     @InjectContainer private ClientElement element;

     @Environmental private JavaScriptSupport javaScriptSupport;

     void afterRender() {
           final JSONObject spec = new JSONObject();
           spec.put("elementId", element.getClientId());
          javaScriptSupport.require("pqc/mixins/DisableAfterSubmit").invoke("initialize").with(spec);
     }
}



DisableAfterSubmit.js

(function() {
     define(["jquery"], function($j) {

           var initialize = function(spec) {
                $j('#' + spec.elementId).on('click', spec, afterClick);
           };

           var afterClick = function(event) {
                // AJAX submit hack: to allow a spinner to be positioned by changing from an input[type=submit] to an anchor element
                // (as Tapestry form submissions via AJAX do not work now except through an input[type=submit])
                var originalElement = $j('#' + event.data.elementId);
                var attrs = {'href':'#'};
                var valueAttr;
                var idAttr = 'hack';
                $j.each(originalElement[0].attributes, function(index, attr) {
                     if (attr.nodeName === 'type') {
                           // No need to add type=submit so continue
                           return;
                     }
                     if (attr.nodeName === 'value') {
                           valueAttr = attr.nodeValue;
                           return;
                     }
                     if (attr.nodeName === 'id') {
                           attrs[attr.nodeName] = attr.nodeValue + idAttr;
                           return;
                     }
                     attrs[attr.nodeName] = attr.nodeValue;
                });
                originalElement.hide();
                originalElement.after(function () {
                    return $j('<a/>', attrs).append(valueAttr);
                });
                $j('#' + event.data.elementId + idAttr).addClass('disabled spinner').attr({'role':'alert', 'aria-busy':'true'}).focus();
           };

         return {
             initialize : initialize
         };
     });
}).call(this);

Cheers,
Niran

PS:
I’m sorry if you receive this email twice as I used different email domain for subscribing to this list and I didn’t see that initial email come through. I have now subscribed using this email address..

From: Ben Weidig <be...@netzgut.net>
Date: Sunday, 26 February 2023 at 14:22
To: Tapestry users <us...@tapestry.apache.org>
Subject: Re: Preventing double click eventlink and form submit
Hi Nathan,

Maybe I misunderstood your goal, but I would solve it with a module
configured by CSS classes or data attributes instead of dedicated
components.

Something along the line of this (untested) CoffeeScript I improvised:


define ['jquery', 't5/core/events'], ($, events) ->

    EVENT = 'click.request-in-progress'

    register = ->
        $('.show-request-in-progress').off(EVENT).on EVENT, ->
            $el = $(this)

            $el.addClass('request-in-progress')

            relatedZone = $el.data('target-zone')
            elEvent = "#{t5events.zone.wilUpdate}.##{$el.id}"
            zone.on elEvent, ->
                zone.off(elEvent)
                trigger.removeClass('request-in-progress')
            return true

    $ ->
        register()
        $(document).on events.zone.didUpdate, register

See https://urldefense.com/v3/__https://gist.github.com/benweidig/2f2fdb32c74cbd57764b1f7dabbe7079__;!!NknhfzgzgQ!yowXeuZLQZ1AaayarMGyXEXL1HzNLMBuDQXODpgTaFvtxz2t3os3D2bD6FeSb24bEnUMv9tTOwG6dX3IZPu215m5$<https://urldefense.com/v3/__https:/gist.github.com/benweidig/2f2fdb32c74cbd57764b1f7dabbe7079__;!!NknhfzgzgQ!yowXeuZLQZ1AaayarMGyXEXL1HzNLMBuDQXODpgTaFvtxz2t3os3D2bD6FeSb24bEnUMv9tTOwG6dX3IZPu215m5$>  for
a nicely formatted version.

This way, you wouldn't need an additional zone and just need to listen to
the content zone to be updated. Only the ".show-request-in-progress" and
"data-target-zone" must be set accordingly.
Actually, the CSS classes isn't needed and the relevant elements could be
targeted via the data attribute.

Cheers
Ben

On Fri, Feb 24, 2023 at 2:11 PM Nathan Quirynen <na...@pensionarchitects.be>
wrote:

> Hi,
>
> I am looking for a solution to prevent multiple clicks on event links or
> form submits in our Tapestry application and in the meanwhile if
> possible giving the user visual feedback while the request is being
> processed.
> We use ajax (async="true") on both our eventlinks and form components,
> but there are no javascript events to hook into right before the request
> is fired and when the request response is handled. But after some
> research I found I can use the zone parameter instead of the async
> parameter and then make use of the t5:zone:refresh and
> t5:zone:did-update events to do exactly that.
> So I came up with following javascript module which can be used for both
> form submit buttons and eventlinks:
>
>              var init = function(triggerClientId, zoneClientId) {
>
>                  var trigger = $('#' + triggerClientId);
>                  var zone = $('#' + zoneClientId);
>
>                  var event_request = 't5:zone:refresh' + '.' +
> triggerClientId;
>
>                  zone.on(event_request, function(e) {
>
>                     // disable trigger + add css class
>                      trigger.css('pointer-events', 'none');
>                      trigger.addClass('request-in-progress');
>
>                      var event_response = 't5:zone:did-update' + '.' +
> triggerClientId;
>                      zone.on(event_response, function() {
>
>                          // remove response event handler
>                          zone.off(event_response);
>
>                          // reenable trigger + remove css class
>                          trigger.css('pointer-events', 'auto');
> trigger.removeClass('request-in-progress');
>
>                      });
>                  });
>
>              }
>
> I would make new components AjaxForm/AjaxEventLink to wrap the original
> ones and implement everything there.
> This seems to work as I hoped it would be, but then a zone element is
> added for every EventLink/Form which seems a bit like overkill, but I
> don't see another way at the moment.
> I was wondering if others have tried to do something like I am, or if
> there's any better way of achieving what I am trying here? Or anything
> that I am missing could be wrong with my implementation?
>
>
> Nathan
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>

Confidentiality note: This e-mail may contain confidential information from Clarivate. If you are not the intended recipient, be aware that any disclosure, copying, distribution or use of the contents of this e-mail is strictly prohibited. If you have received this e-mail in error, please delete this e-mail and notify the sender immediately.

Re: Preventing double click eventlink and form submit

Posted by Ben Weidig <be...@netzgut.net>.
Hi Nathan,

Maybe I misunderstood your goal, but I would solve it with a module
configured by CSS classes or data attributes instead of dedicated
components.

Something along the line of this (untested) CoffeeScript I improvised:


define ['jquery', 't5/core/events'], ($, events) ->

    EVENT = 'click.request-in-progress'

    register = ->
        $('.show-request-in-progress').off(EVENT).on EVENT, ->
            $el = $(this)

            $el.addClass('request-in-progress')

            relatedZone = $el.data('target-zone')
            elEvent = "#{t5events.zone.wilUpdate}.##{$el.id}"
            zone.on elEvent, ->
                zone.off(elEvent)
                trigger.removeClass('request-in-progress')
            return true

    $ ->
        register()
        $(document).on events.zone.didUpdate, register

See https://gist.github.com/benweidig/2f2fdb32c74cbd57764b1f7dabbe7079 for
a nicely formatted version.

This way, you wouldn't need an additional zone and just need to listen to
the content zone to be updated. Only the ".show-request-in-progress" and
"data-target-zone" must be set accordingly.
Actually, the CSS classes isn't needed and the relevant elements could be
targeted via the data attribute.

Cheers
Ben

On Fri, Feb 24, 2023 at 2:11 PM Nathan Quirynen <na...@pensionarchitects.be>
wrote:

> Hi,
>
> I am looking for a solution to prevent multiple clicks on event links or
> form submits in our Tapestry application and in the meanwhile if
> possible giving the user visual feedback while the request is being
> processed.
> We use ajax (async="true") on both our eventlinks and form components,
> but there are no javascript events to hook into right before the request
> is fired and when the request response is handled. But after some
> research I found I can use the zone parameter instead of the async
> parameter and then make use of the t5:zone:refresh and
> t5:zone:did-update events to do exactly that.
> So I came up with following javascript module which can be used for both
> form submit buttons and eventlinks:
>
>              var init = function(triggerClientId, zoneClientId) {
>
>                  var trigger = $('#' + triggerClientId);
>                  var zone = $('#' + zoneClientId);
>
>                  var event_request = 't5:zone:refresh' + '.' +
> triggerClientId;
>
>                  zone.on(event_request, function(e) {
>
>                     // disable trigger + add css class
>                      trigger.css('pointer-events', 'none');
>                      trigger.addClass('request-in-progress');
>
>                      var event_response = 't5:zone:did-update' + '.' +
> triggerClientId;
>                      zone.on(event_response, function() {
>
>                          // remove response event handler
>                          zone.off(event_response);
>
>                          // reenable trigger + remove css class
>                          trigger.css('pointer-events', 'auto');
> trigger.removeClass('request-in-progress');
>
>                      });
>                  });
>
>              }
>
> I would make new components AjaxForm/AjaxEventLink to wrap the original
> ones and implement everything there.
> This seems to work as I hoped it would be, but then a zone element is
> added for every EventLink/Form which seems a bit like overkill, but I
> don't see another way at the moment.
> I was wondering if others have tried to do something like I am, or if
> there's any better way of achieving what I am trying here? Or anything
> that I am missing could be wrong with my implementation?
>
>
> Nathan
>
>
> ---------------------------------------------------------------------
> To unsubscribe, e-mail: users-unsubscribe@tapestry.apache.org
> For additional commands, e-mail: users-help@tapestry.apache.org
>
>