Hat man viele Felder in einem Formular, ist es manchmal sinnvoll, diese logisch zu strukturieren. Dies kann z. B. durch eine Aufteilung in Reiter, nachfolgend „Tabs“ genannt, realisiert werden. Damit die Daten an den Server übertragen werden, braucht man zusätzlich ein HTML Formular Element. Bevor die Daten dann verarbeit werden können, sollten diese zuvor noch zusätzlich validiert werden! Genau an diesem Punkt stellen sich ein paar Fragen:

Wie zeige ich am Besten die Fehlermeldungen an?
Was ist, wenn auf einem Tab, dass nicht gerade aktiv ist, Fehler auftreten?

Man könnte jetzt umständlich versuchen immer das richtige Tab zu aktvieren, dass einen Fehler enthält.
Dies führt aber spätestens dann zu Problemen, wenn in unterschiedlichen Tabs Fehler auftreten.

Lösungvorschlag

Folgender schöne Lösungsansatz möchte ich in diesem Beitrag beschreiben. Man könnte bereits bei einem Tab-Wechsel die Formular-Daten des aktuellen Tabs an den Server übertragen und auch validieren lassen! Schlagen dann Validierungen fehl, wird das Tab nicht gewechselt und die Fehlermeldungen werden angezeigt. Nachdem diese Funktion leider aktuell in der PrimeFaces TabView-Komponente nicht implementiert ist, muss hier ein bisschen Eigenleistung erbracht und die Funktionalität erweitert werden.

package com.bloggingit.view;

import java.io.Serializable;
import javax.inject.Named;
import javax.faces.view.ViewScoped;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
import javax.validation.constraints.Size;

/**
 * @author blogging-it.com
 */
@Named
@ViewScoped
public class SubmitAndValidateBean implements Serializable {

    private static final long serialVersionUID = 1L;

    @NotNull
    @Size(min = 3)
    private String txt_field_1;

    @NotNull
    @Min(3)
    private Integer nb_field_2;

    public String getTxt_field_1() {
        return txt_field_1;
    }

    public Integer getNb_field_2() {
        return nb_field_2;
    }

    public void setTxt_field_1(String txt_field_1) {
        this.txt_field_1 = txt_field_1;
    }

    public void setNb_field_2(Integer nb_field_2) {
        this.nb_field_2 = nb_field_2;
    }

    public String submit() {
        System.out.printf("txt_field_1: %s nb_field_2: %d", txt_field_1, nb_field_2);
        return null;
    }
}

Als Basis dient uns diese Bean-Klasse. Diese bietet zwei Attribute „txt_field_1“ und „nb_field_2“ mit den dazugehörigen Getter- und Setter-Methoden. Die Methode „submit()“ dient als Action-Methode für unseren Submit-Button und nur gibt die angegebenen Werte unserer Attribute aus. Damit zusätzlich eine Valdierung unserer Attribute stattfindet, wurden diese jeweils mit Annoationen versehen.
@NotNull – sagt aus, dass der Wert des Attributs nie NULL liefern darf
@Size(min = 3) – sagt aus, dass der Wert des Attributs mindestens 3 Zeichen lang sein muss
@Min(3) – sagt aus, dass der Wert des Attributs mindestens 3 oder größer sein muss

    <context-param>
        <param-name>javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL</param-name>
        <param-value>true</param-value>
    </context-param>  

Damit die @NotNull Validierung korrekt funktioniert, muss in der web.xml Datei der JavaServer Faces Context-Parameter „javax.faces.INTERPRET_EMPTY_STRING_SUBMITTED_VALUES_AS_NULL“ auf den Wert „true“ gesetzt werden.

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" 
    "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:p="http://primefaces.org/ui" xmlns:h="http://java.sun.com/jsf/html">
    <h:head>
        <title>Submit Tab &amp; Validate</title>
        <script type="text/javascript" src="functions.js"></script>
    </h:head>
    <h:body>
        <h3>Submit Tab &amp; Validate</h3>
        <p:tabView id="tabView" widgetVar="tabView_wid" styleClass="tabView">  
            <p:tab id="tab1" title="Tab 1">  
                <h:form id="form1">
                    <h:panelGrid columns="3" cellpadding="10">  
                        <h:outputLabel id="lbl1" value="Textfield 1*" for="txt_field_1" />

                        <h:inputText id="txt_field_1"
                                     label="Textfield 1" 
                                     value="#{submitAndValidateBean.txt_field_1}" />
                        <p:message for="txt_field_1" />

                        <h:outputText value="Submitted value" />
                        <h:outputText value="#{submitAndValidateBean.txt_field_1}" /><br/>
                    </h:panelGrid>
                </h:form>
            </p:tab>
            <p:tab id="tab2" title="Tab 2"> 
                <h:form id="form2">
                    <h:panelGrid columns="3" cellpadding="10">
                        <h:outputLabel id="lbl2" value="Numberfield 2*" for="nb_field_2"/>
                        <h:inputText id="nb_field_2" 
                                     label="Numberfield 2" 
                                     value="#{submitAndValidateBean.nb_field_2}" />
                        <p:message for="nb_field_2"/>

                        <h:outputText value="Submitted value" />
                        <h:outputText value="#{submitAndValidateBean.nb_field_2}" /><br/>
                    </h:panelGrid>
                </h:form>
            </p:tab>
        </p:tabView>

        * mandatory fields

        <h:form id="formSubmit" style="margin-top: 10px;">
            <p:commandButton 
                value="Submit" 
                action="#{submitAndValidateBean.submit}" 
                partialSubmit="true" 
                process="@this @(.tabView .ui-tabs-panel:visible > form)"  
                update="@(.tabView .ui-tabs-panel:visible > form)" />
        </h:form>

        <h:form prependId="true">
            <p:remoteCommand name="rc_tabview_submit" partialSubmit="true" global="false"
                             process="@(.tabView .ui-tabs-panel:visible > form)" 
                             update="@(.tabView .ui-tabs-panel:visible > form)"
                             oncomplete="if(!args.validationFailed){tabView_wid.select(tabView_wid.clickedTab);}"/>   
        </h:form>

        <div id="footer" style="font-weight: bold; margin-top: 25px;">
            Copyright by <a href="http://www.blogging-it.com" target="_blank" title="blogging-it.com">blogging-it.com</a>
        </div>
    </h:body>
</html>

In diesem Beispiel wird eine PrimeFaces TabView (p:tabView) mit zwei Tabs (p:tab) erzeugt. Die Variablen des Beans wurden jeweils in einem eigenen Tab eingefügt. Jeder dieser Tabs enthält eine eigene Form (h:form). Dies ist notwendig, da immer nur die Daten des aktuellen Tabs an den Server gesendet werden sollen.

    <p:commandButton 
        value="Submit" 
        action="#{submitAndValidateBean.submit}" 
        partialSubmit="true" 
        process="@this @(.tabView .ui-tabs-panel:visible > form)"  
        update="@(.tabView .ui-tabs-panel:visible > form)" />

Der Submit-Button (p:commandButton) befindet sich außerhalb der TabView in einer eigenen Form. Dieser sendet über einen PrimeFaces Selector @(.tabView .ui-tabs-panel:visible > form) immer nur die Felder des aktiven Tabs an den Server. Denn wenn das Tab nicht gewechselt wird, müssen die Daten des aktiven Tabs auch an den Server gesendet werden. Beim Attribut „update“ muss der gleiche Selector eingetragen werden, damit im Fehlerfall der HTML Code erneuert wird, um die Fehlermeldungen anzuzeigen.

        <h:form prependId="true">
            <p:remoteCommand name="rc_tabview_submit" partialSubmit="true" global="false"
                             process="@(.tabView .ui-tabs-panel:visible > form)" 
                             update="@(.tabView .ui-tabs-panel:visible > form)"
                             oncomplete="if(!args.validationFailed){tabView_wid.select(tabView_wid.clickedTab);}"/>   
        </h:form>

Zusätzlich muss noch eine RemoteCommand Komponente (p:remoteCommand) eingebunden werden. Diese wird später über JavaScript aufgerufen, sobald ein Tab-Wechsel ausgeführt wird. Genau wie beim Submit-Button, werden dann auch wieder nur die Felder des aktiven Tabs an den Server gesendet und im Fehlerfall die Fehlermeldungen angezeigt! Damit der Tab nur gewechselt wird wenn keine Fehler aufgetreten sind, muss unter „oncomplete“ diese Prüfung „if(!args.validationFailed)“ durchgeführt werden.

Um jetzt die TabView Komponente so zu erweitern, dass bei einem Tab-Wechsel die Daten an den Server gesendet werden, ist ein bisschen JavaScript Magie notwendig. Der Code wird im Beispiel über die Datei „functions.js“ eingebunden.

jQuery(function() {
    tabView_bind_onClick(rc_tabview_submit, tabView_wid);
});

function tabView_bind_onClick(rc, widget) {
    // unbind PrimeFaces click handler and bind own
    widget.jq.find('.ui-tabs-nav > li').unbind('click.tabview').
            off('click.custom-tabview').on('click.custom-tabview', function(e) {
        var element = jQuery(this);
        if (jQuery(e.target).is(':not(.ui-icon-close)')) {
            var index = element.index();
            if (!element.hasClass('ui-state-disabled') && index !== widget.cfg.selected) {
                widget.clickedTab = element.index(); // buffer clicked tab index
                var id;
                if (element.id) {
                    id = element.id();
                }

                rc(); // execute the remote command
            }
        }

        e.preventDefault();
    });
}

Über die jQuery document-ready Funktion wird die eigene Funktion „tabView_bind_onClick()“ ausgeführt, sobald der DOM komplett geladen ist. Als Parameter bekommt die Funktion, die WidgetVar der RemoteCommand und die WidgetVar der TabView übergeben.

In der Funktion „tabView_bind_onClick()“ wird als erstes über die unbind() Funktionalität, der orginale Event-Handler entfernt! Danach wird eine eigene Event-Handler Funktion über on() hinzugefügt. Die Event-Handler Funktion führt ein paar Prüfungen durch, z. B. ob das geklickte Tab deaktiviert ist! Falls alles passt, wird mittels „rc();“ Befehl die zuvor angelegte RemoteCommand Komponente aufgerufen.

Beispieldateien

Submit-Tab-And-Validate-1.0 Source-Code (Netbeans Projekt)
Submit-Tab-And-Validate-1.0 WAR Datei

PrimeFaces TabView: Formular-Felder beim Tab-Wechsel senden und validieren

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht. Erforderliche Felder sind mit * markiert.