Lassen Sie den Dialog p: geöffnet, wenn nach dem Senden ein Validierungsfehler auftritt

70

Minimaler Beispieldialog:

<p:dialog header="Test Dialog"  
          widgetVar="testDialog"> 
  <h:form> 
    <p:inputText value="#{mbean.someValue}"/> 

    <p:commandButton value="Save" 
                     onsuccess="testDialog.hide()" 
                     actionListener="#{mbean.saveMethod}"/> 
  </h:form>       
</p:dialog> 

Ich möchte in der Lage sein, die mbean.saveMethod irgendwie daran zu hindern, den Dialog zu schließen, wenn es ein Problem gab, und eine Nachricht nur durch Knurren auszugeben. Dies ist ein Fall, in dem ein Validator nicht hilft, da nicht festgestellt werden kann, ob someValue gültig ist, bis ein Speicher an einen Back-End-Server gesendet wird. Derzeit mache ich dies mit dem sichtbaren Attribut und zeige es auf ein boolesches Feld in mbean. Das funktioniert, aber es macht die Benutzeroberfläche langsamer, da das Auf- oder Abblättern des Dialogfelds das Drücken des Servers erfordert.

JOTN
quelle

Antworten:

158

Die onsuccessLäufe , wenn Ajax - Request selbst erfolgreich ist (dh es gibt keine Netzwerkfehler, abgefangene Ausnahme, etc.), nicht , wenn Aktion Methode wurde erfolgreich aufgerufen.

Bei einem gegebenen <p:dialog widgetVar="testDialog">, könnten Sie das entfernen onsuccessund ersetzen Sie es durch PrimeFaces RequestContext#execute()innen saveMethod():

if (success) {
    RequestContext.getCurrentInstance().execute("PF('testDialog').hide()");
}

Hinweis: PF()wurde in PrimeFaces 4.0 eingeführt. In älteren PrimeFaces-Versionen benötigen Sie testDialog.hide()stattdessen.

Wenn Sie den Controller nicht mit ansichtsspezifischen Skripten überladen möchten, können Sie oncompletestattdessen ein argsObjekt mit einer booleschen validationFailedEigenschaft verwenden:

<p:commandButton ...
    oncomplete="if (args &amp;&amp; !args.validationFailed) PF('testDialog').hide()" />

Die if (args)Überprüfung ist erforderlich, da sie möglicherweise nicht vorhanden ist, wenn ein Ajax-Fehler aufgetreten ist, und daher einen neuen JS-Fehler verursacht, wenn Sie versuchen, validationFailedihn zu beheben. Das &amp;Statt von &ist aus dem in dieser Antwort erläuterten Grund obligatorisch. Refaktorieren Sie gegebenenfalls eine JS-Funktion, die Sie aufrufen, wie oncomplete="hideDialogOnSuccess(args, testDialog)"in Siehe <p: ​​dialog> geöffnet, wenn die Validierung fehlgeschlagen ist .

Wenn es jedoch keine Validierungsfehler und die Aktionsmethode erfolgreich ausgelöst, und Sie möchten , dass nach wie vor den Dialog offen zu halten , weil zB eine Ausnahme im Dienst - Methodenaufruf, dann können Sie manuell Trigger validationFailedzu truevon innen Backing Bean Action - Methode explizit aufrufen FacesContext#validationFailed(). Z.B

FacesContext.getCurrentInstance().validationFailed();
BalusC
quelle
2
Die Verwendung von RequestContext ist ziemlich interessant. Ich wusste nicht, dass du das kannst.
JOTN
1
Der Ausdruck in oncomplete muss negiert werden. oncomplete = "if (args.validationFailed) ..."
Steven Shaw
1
@ BalusC oncomplete="if(args &amp;&amp; !args.validationFailed)das ist meine Art es zu tun. ist nullCheck in meinem Code unnötige?
Kerem Baydoğan
1
@BalusC, warum in diesem Fall Actionlistener anstelle von Action verwenden?
Mahmoud Saleh
2
@ Mah: Ich habe gerade den ursprünglichen Code von OP übernommen. Bei der Frage ging es nicht um actionListener vs action, daher habe ich den ursprünglichen Code von OP unverändert beibehalten. Aber ich stimme zu, dass dies nicht der empfohlene Weg ist, für den Fall, dass Sie sich fragen.
BalusC
16

Ich habe gerade diese Lösung gegoogelt . Grundsätzlich besteht die Idee darin, actionListener anstelle der Aktion der Schaltfläche zu verwenden. In der Backing-Bean fügen Sie einen Rückrufparameter hinzu, der dann in der Oncomplete-Methode der Schaltfläche überprüft wird. Beispielteilcode:

JSF zuerst:

<p:commandButton actionListener="#{myBean.doAction}"
   oncomplete="if (!args.validationFailed &amp;&amp; args.saved) schedulesDialog.hide();" />

Backing Bean:

public void doAction(ActionEvent actionEvent) {
    // do your stuff here...
    if (ok) {
        RequestContext.getCurrentInstance().addCallbackParam("saved", true);
    } else {
        RequestContext.getCurrentInstance().addCallbackParam("saved", false);
    }
}

Hoffe das hilft jemandem :)

Soltysh
quelle
Link verändert hier .
Vhunsicker
15

Die Verwendung des oncompleteAttributs über Ihre Befehlsschaltfläche und eines wirklich einfachen Skripts hilft Ihnen sehr.

Ihr Dialog und Ihre Befehlsschaltfläche sehen ungefähr so ​​aus:

<p:dialog widgetVar="dialog">
   <h:form id="dialogView">
       <p:commandButton id="saveButton" icon="ui-icon-disk"
           value="#{ui['action.save']}"
           update=":dataList :dialogView"
           actionListener="#{mbean.save()}"
           oncomplete="handleDialogSubmit(xhr, status, args)" />
   </h:form>
 </p:dialog>

Und das Skript wäre ungefähr so:

<script type="text/javascript">
    function handleDialogSubmit(xhr, status, args) {
        if (args.validationFailed) {
            dialog.show();
        } else {
            dialog.hide();
        }
    }
</script>
Alonso Dominguez
quelle
7

Ich benutze diese Lösung:

JSF-Code:

<p:dialog ... widgetVar="dlgModify" ... >
...
<p:commandButton value="Save" update="@form" actionListener="#{AdminMB.saveTable}" />
<p:commandButton value="Cancel" oncomplete="PF('dlgModify').hide();"/>

Backing Bean Code:

public void saveTable() {
    RequestContext rc = RequestContext.getCurrentInstance();
    rc.execute("PF('dlgModify').hide()");
}
Antaeus
quelle
3

Ich glaube, das ist die sauberste Lösung. Dazu müssen Sie den Schaltflächencode nicht ändern . Diese Lösung überschreibt den Prototyp der Funktion "Ausblenden".

$(document).ready(function() {
    PrimeFaces.widget.Dialog.prototype.originalHide = PrimeFaces.widget.Dialog.prototype.hide; // keep a reference to the original hide()
    PrimeFaces.widget.Dialog.prototype.hide = function() {
        var ajaxResponseArgs = arguments.callee.caller.arguments[2]; // accesses oncomplete arguments
        if (ajaxResponseArgs && ajaxResponseArgs.validationFailed) {
            return;  // on validation error, prevent closing
        }
        this.originalHide();
    };
});

Auf diese Weise können Sie Ihren Code wie folgt behalten:

<p:commandButton value="Save" oncomplete="videoDetalheDialogJS.hide();" 
   actionListener="#{videoBean.saveVideo(video)}" />
Luís Soares
quelle
2

Die einfachste Lösung besteht darin, kein "widget.hide" zu haben, weder in onclick noch in oncomplete. Entfernen Sie die Versteckfunktionen und setzen Sie sie einfach ein

visible="#{facesContext.validationFailed}" 

für das Dialog-Tag

makkasi
quelle
Du hast meinen Tag nach drei gemacht. Dank dafür!
CleitonCardoso