web-dev-qa-db-fra.com

Comment faire de la prévention par double-clic dans JSF 2

Nous avons quelques pages de recherche qui utilisent beaucoup de données et prennent un certain temps à compléter. Lorsqu'un utilisateur clique sur le bouton de recherche, nous souhaitons ne pas lui permettre de soumettre le résultat de la recherche une seconde fois. 

Existe-t-il une meilleure pratique pour effectuer la détection/prévention "double-clic" dans JSF? 

Le composant PrimeFaces semble pouvoir faire ce que nous voulons, car il désactivera l'interface utilisateur pendant un certain temps entre le moment où le bouton de recherche est cliqué et celui où la recherche est terminée, mais existe-t-il une stratégie plus générique que nous pouvons utiliser (peut-être quelque chose qui ne dépendants de PrimeFaces)? Idéalement, tout clic sur le bouton sera soit désactivé, soit ignoré jusqu'à la fin de la recherche. Nous n'avons pas nécessairement besoin de désactiver toute l'interface utilisateur (comme le permet blockUI).

24
BestPractices

Si vous utilisez uniquement des requêtes ajax, vous pouvez utiliser le gestionnaire jsf.ajax.addOnEvent de l'API JavaScript JSF pour cela. L'exemple ci-dessous s'appliquera à tous les boutons de type="submit".

function handleDisableButton(data) {
    if (data.source.type != "submit") {
        return;
    }

    switch (data.status) {
        case "begin":
            data.source.disabled = true;
            break;
        case "complete":
            data.source.disabled = false;
            break;
    }    
}

jsf.ajax.addOnEvent(handleDisableButton);

Si vous n'en avez besoin que sur des boutons spécifiques, vous pouvez également utiliser l'attribut onevent de <f:ajax>.

<h:commandButton ...>
    <f:ajax ... onevent="handleDisableButton" />
</h:commandButton>

Si vous devez également appliquer cela aux requêtes synchrones, vous devez tenir compte du fait que lorsque vous désactivez un bouton pendant onclick, la paire name=value du bouton ne sera pas envoyée en tant que paramètre de requête et par conséquent, JSF ne pourra pas identifier. l'action et l'invoquer. Vous ne devez donc le désactiver que après que la demande POST ait été envoyée par le navigateur. Il n’existe pas de gestionnaire d’événements DOM pour cela; vous devez utiliser le hack setTimeout() qui désactive le bouton ~ 50 ms après un clic.

<h:commandButton ... onclick="setTimeout('document.getElementById(\'' + this.id + '\').disabled=true;', 50);" />

Ceci est seulement plutôt fragile. Il est peut-être trop court pour les clients lents. Vous devez augmenter le délai d'attente ou passer à une autre solution.

Cela dit, gardez à l'esprit que cela empêche uniquement les doubles soumissions lorsque vous les soumettez par une page Web. Cela n'empêche pas les clients HTTP programmés de soumettre en double, comme URLConnection, Apache HttpClient, Jsoup, etc. Si vous souhaitez imposer l'unicité du modèle de données, vous ne devez pas empêcher les doubles envois, mais empêcher les doubles insertions. En SQL, cela peut être facilement réalisé en appliquant une contrainte UNIQUE sur la ou les colonne (s) d’intérêt.

Voir également:

33
BalusC

je suis tombé sur cette question, ayant le même problème. La solution n'a pas fonctionné pour moi - après un bref aperçu de primefaces.js je suppose qu'ils n'utilisent plus jsf.ajax.

donc je devais trouver quelque chose par moi-même et voici ma solution, pour les personnes qui ne peuvent pas non plus utiliser celle ci-dessus:

// we override the default send function of
// primeFaces here, so we can disable a button after a click
// and enable it again after     

var primeFacesOriginalSendFunction = PrimeFaces.ajax.AjaxUtils.send;

PrimeFaces.ajax.AjaxUtils.send = function(cfg){    
  var callSource = '';

  // if not string, the caller is a process - in this case we do not interfere

  if(typeof(cfg.source) == 'string') {

    callSource = jQuery('#' + cfg.source);
    callSource.attr('disabled', 'disabled');

  }



  // in each case call original send
  primeFacesOriginalSendFunction(cfg);

  // if we disabled the button - enable it again
  if(callSource != '') {

    callSource.attr('disabled', 'enabled');

  }

};
1
user2862701

Vous pouvez utiliser des écouteurs 'onclick' et 'oncomplete'. Lorsque l'utilisateur clique sur le bouton - le désactiver. Lorsque l'action est terminée - activez.

<p:commandButton id="saveBtn" onclick="$('#saveBtn').attr('disabled',true);" oncomplete="$('#saveBtn').attr('disabled',false);" actionListener="#{myBean.save}" />
1
Vladimir Tsukanov

solution très utile jsf-primefaces, utilisée avec le modèle facelets étendu à d'autres pages consommateurs

<f:view>
    <Script language="javascript">
        function checkKeyCode(evt)
        {
            var evt = (evt) ? evt : ((event) ? event : null);
            var node = (evt.target) ? evt.target : ((evt.srcElement) ? evt.srcElement : null);
            if(event.keyCode==116)
            {
                evt.keyCode=0;
                return false
            }
        }
        document.onkeydown=checkKeyCode;

        function handleDisableButton(data) {
            if (data.source.type != "submit") {
                return;
            }

            switch (data.status) {
                case "begin":
                    data.source.disabled = true;
                    break;
                case "complete":
                    data.source.disabled = false;
                    break;
            }    
        }
        jsf.ajax.addOnEvent(handleDisableButton);
    </Script>

</f:view>

<h:head>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link href="./resources/css/default.css" rel="stylesheet" type="text/css" />
    <link href="./resources/css/cssLayout.css" rel="stylesheet" type="text/css" />
    <title>infoColegios - Bienvenido al Sistema de Administracion</title>
</h:head>

<h:body onload="#{login.validaDatos(e)}">
    <p:layout fullPage="true">

        <p:layoutUnit position="north" size="120" resizable="false" closable="false" collapsible="false">                   
            <p:graphicImage value="./resources/images/descarga.jpg" title="imagen"/> 
            <h:outputText value="InfoColegios - Bienvenido al Sistema de Administracion" style="font-size: large; color: #045491; font-weight: bold"></h:outputText>                    
        </p:layoutUnit>

        <p:layoutUnit position="west" size="175" header="Nuestra Institución" collapsible="true" effect="drop" effectSpeed="">
            <p:menu> 
                <p:submenu>
                    <p:menuitem value="Quienes Somos" url="http://www.primefaces.org/showcase-labs/ui/home.jsf" />

                </p:submenu>
            </p:menu>
        </p:layoutUnit>

        <p:layoutUnit position="center">
            <ui:insert name="content">Content</ui:insert>
        </p:layoutUnit>

    </p:layout>
</h:body>

0
user1947165

Fait un travail simple avec hide and show, fonctionne bien avec les éléments ayant un type d’entrée submit Jquery

$(":submit").click(function (event) {
        // add exception to class skipDisable 
        if (!$(this).hasClass("skipDisable")) {
            $(this).hide();
			$(this).after("<input type='submit' value='"+$(this).val()+"' disabled='disabled'/>");
        }
    });
     <script src="https://ajax.googleapis.com/ajax/libs/jquery/1.8.1/jquery.min.js"></script>
        <form>
        <input type="submit" value="hello">
        </form>
0
Anupam

Aucune des solutions ci-dessus n'a fonctionné pour moi (j'ai vraiment essayé chacune d'elles). Mon formulaire a toujours été envoyé deux fois lorsque l'utilisateur a double-cliqué sur le bouton de connexion. Je travaille avec JSF (Mojarra 2.1.6) sur Glassfish 3.1.2.

Considérez que c'était une page de connexion non-AJAX.

Alors voici comment je l'ai résolu: 

  1. définissez une variable JavaScript globale pour contrôler la soumission dans l'en-tête de page ou n'importe où en dehors de votre formulaire:
var submitting = false;
  1. réglez-le sur true lorsque submit h:form onsubmit est déclenché:
<h:form onsubmit="submitting = true">
  1. Vérifiez la valeur de la variable sur l'événement de clic de h: commandLink:
<h:commandLink ... onclick="if(submitting){return false}">

Ceci est juste une autre alternative simple et il a été testé dans Chrome [Version 47.0.2526.106 (64 bits)], Mozilla Firefox (37.0.2) et Internet Explorer 11. J'espère que cela aidera quelqu'un.

0
Jaumzera