Java Server Faces (JSF) und Ajax - Java EE6
- Einleitung - Teil 1
- Servlets - Teil 2
- JSP-Seiten - Teil 3
- JSP-Seiten mit JSTL - Teil 4
- JSF-Seiten - Teil 5
- JSF-Seiten mit Ajax - Teil 6
- JSF-Templates - Teil 7
20.01.2013
In diesem Artikel wird kurz beschrieben, wie man eine JSF-Seite um Ajax erweitert, so dass bei Änderungen nicht die komplette Seite neu geladen werden muss, sondern nur der Teil, der sich auch wirklich geändert hat. Dies wird an dem "bekannten" Beispiel eines Einkaufswagens (Shopping-Cart) erklärt. Dieser Artikel ist keine Einführung in die JSF-Programmierung oder in Ajax. Er soll vielmehr zeigen, wie die grundlegende Funktionsweise von JSF-Seiten und Ajax ist. In den folgenden Artikeln zum Thema Java EE6 Web-Client werden andere Technologien vorgestellt, um Web-Oberflächen und Web-Clients zu erstellen.
Die fertige Anwendung, die in diesem Artikel erstellt wird, sieht im Browser folgendermaßen aus:
Auf der linken Seite lassen sich Artikel auswählen und über die kleinen Pfeile in den Einkaufswagen auf der rechten Seiten legen oder auch wieder entfernen. Mit dem Button Warenkorb leeren werden alle Artikel aus dem Warenkorb entfernt und wieder in der Artikelliste angezeigt.
Die folgende Abbildung zeigt den Ablauf einer Anfrage an den Web-Container und die Antwort an den Client.
Man sieht, dass ein Request vom Browser, in dem der Web-Client läuft, an den Web-Container, in dem das Faces-Servlet läuft, gestellt wird. Das Faces-Servlet dient in diesem Kontext als Controller und arbeitet die Business-Logik ab. Dann wird das Erstellen der Antwort an die JSF-Seite weitergeleitet. Die View, die der Web-Container an den Browser als Response zurückgeliefert, wird also durch das Verarbeiter der JSF-Seite erstellt. Wichtig ist, dass beim Übergang vom Faces-Servlet zur JSF-Seite, der JSF-Lifecycle durchlaufen wird. Das Ergebnis ist eine fertige HTML-Seite, die an den Browser gesendet werden kann. (Anmerkung: Die Darstellung ist sehr vereinfacht. Der JSF-Lifecycle wäre einen eigenen Artikel wert.)
Der zugehörige Java-Quellcode sieht folgendermaßen aus:
package org.hameister.shoppingcarts; import java.io.Serializable; import java.util.ArrayList; import java.util.List; import javax.annotation.PostConstruct; import javax.enterprise.context.SessionScoped; import javax.faces.event.ActionEvent; import javax.inject.Named; /** * * @author hameister */ @Named @SessionScoped public class CartJSFAjax implements Serializable { // All available items private List<String> items; // List of items in the cart private List<String> shoppingCart; private String[] selectedItems; private String[] selectedCartItems; @PostConstruct private void initLists() { items = new ArrayList<String>(); shoppingCart = new ArrayList<String>(); items.add("iPhone 5"); items.add("iPhone 4S"); items.add("iPad mini"); items.add("MacBook Pro Retina"); items.add("MacBook Pro"); } public void putInShoppingCart(ActionEvent e) { if(selectedItems != null) { for(String item : selectedItems) { items.remove(item); shoppingCart.add(item); } } } public void removeFromShoppingCart(ActionEvent e) { if(selectedCartItems != null) { for(String item : selectedCartItems) { shoppingCart.remove(item); items.add(item); } } } public List<String> getItems() { return items; } public List<String> getShoppingCart() { return shoppingCart; } public String[] getSelectedItems() { return selectedItems; } public void setSelectedItems(String[] selectedItems) { this.selectedItems = selectedItems; } public String[] getSelectedCartItems() { return selectedCartItems; } public void setSelectedCartItems(String[] selectedCartItems) { this.selectedCartItems = selectedCartItems; } public void reset() { initLists(); } }
Was bei dieser Klasse sofort auffällt ist, dass sie sehr einfach und übersichtlich ist. (im Vergleich zu der JSP- oder auch Servlet-Lösung). Über die Annotation @Named
wird mittels Dependency Injection dafür gesorgt, dass die Klasse von der JSF-Seite erreichbar ist. Und die Annotation @SessionScoped
ist dafür zuständig, dass für jede neue Session ein neues Objekt der Klasse erstellt wird.
Zu beachten sind außerdem die Funktionen putInShoppingCart
und removeFromShoppingCart
, die aufgerufen werden, wenn die Pfeil-Buttons angeklickt werden.
Diese beiden Funktionen widerum greifen auf Arrays zu, in denen sich selektierte Artikel (items
) befinden. In dem Array selectedCartItems
sind alle ausgewählten Artikel des Einkaufswaren enthalten. Das Array selectedItems
enthält die selektierten Artikel aus der Artikelliste. Die vier Methoden, die die selektierten Artikel setzen und abfragen, werden durch die <h:selectManyListbox>
-Komponenten beim Auswählen von Artikeln durch den Benutzer der JSF-Seite angesprochen.
Die folgende XML-Datei enthält die JSF-Seite.
<?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:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core"> <h:head> <title>Ajax Shopping Cart</title> <h:outputStylesheet library="css" name="cart.css"/> </h:head> <h:body> <h:form id="shoppingCartForm"> <h:panelGrid styleClass="cartTable" columns="3" rowClasses="cartRows"> <!-- ZEILE 1 --> <h:outputLabel value="Artikel Liste" styleClass="cartHeader"/> <h:outputLabel value="" styleClass="cartHeader"/> <h:outputLabel value="Warenkorb" styleClass="cartHeader"/> <!-- ZEILE 2 --> <h:selectManyListbox id="itemList" value="#{cartJSFAjax.selectedItems}" styleClass="cartSelect" size="4"> <f:selectItems value="#{cartJSFAjax.items}"/> </h:selectManyListbox> <h:panelGroup id="buttons"> <h:commandButton id="removeButton" value="←" actionListener="#{cartJSFAjax.removeFromShoppingCart(e)}"> <!-- execute contains informations for the server; selected items of the selectManyListbox with the id cartItemList; call actionlistener --> <!-- render contains all elements which must be rendered after the change --> <f:ajax execute="shoppingCartForm:cartItemList" render="shoppingCartForm:itemList shoppingCartForm:cartItemList"/> </h:commandButton> <h:commandButton id="addButton" value="→" actionListener="#{cartJSFAjax.putInShoppingCart(e)}"> <f:ajax execute="shoppingCartForm:itemList" render="shoppingCartForm:itemList shoppingCartForm:cartItemList"/> </h:commandButton> </h:panelGroup> <h:selectManyListbox id="cartItemList" value="#{cartJSFAjax.selectedCartItems}" styleClass="cartSelect" size="4"> <f:selectItems value="#{cartJSFAjax.shoppingCart}"/> </h:selectManyListbox> <!-- ZEILE 3 --> <h:outputLabel value=""/> <h:outputLabel value=""/> <h:commandButton id="resetButton" value="Warenkorb leeren" actionListener="#{cartJSFAjax.reset}"> <f:ajax execute="shoppingCartForm:cartItemList shoppingCartForm:itemList" render="shoppingCartForm:itemList shoppingCartForm:cartItemList"/> </h:commandButton> </h:panelGrid> </h:form> </h:body> </html>
Zum Aufbau der JSF-Seite läßt sich erstmal folgendes sagen:
<form>
mit dem Attribut id
und dem Wert shoppingCartForm
. In diese ist ein <h:panelGrid>
mit drei Spalten eingebettet. Die erste Zeile des <h:panelGrid>
enthält drei <h:outputLabel>
(ein Label für jede Spalte).<h:selectManyListbox>
, einer <h:panelGroup>
und noch einer <h:selectManyListbox>
.<h:outputLabel>
s ohne Inhalt und einen <h:commandButton>
.
Die <h:selectManyListbox>
en funktionieren folgendermaßen:
id
damit aus der <h:panelGroup>
darauf zugegriffen werden kann.value
werden die selektierten Elemente bei der Bean abgefragt. (Aufruf der getter
und setter
).styleClass
greift auf die css
-Datei (siehe unten) zu und sorgt für das Aussehen der Komponente.size
legt fest, dass nur 4 Zeilen in der Listbox zu sehen sind.<h:selectManyListbox>
ist ein Element selectItems
enthalten, welches über das Attribut value
die Werte vom Bean abfragt.
Die <h:panelGroup>
besteht aus zwei <h:commandButton>
s mit den kleinen Pfeil-Symbolen, die ein Verschieben der Artikel ermöglichen. Funktionieren tun die Buttons folgendermaßen:
id
ist in dem Beispiel eher unwichtig.value
enthält den Text des Buttons. In dem Beispiel sind es die kleinen Pfeile.actionListener
, der beim Klick auf einen Button die Methode im Bean aufruft.<h:commandButton>
s befindet sich das Ajax-Element, welches dafür sorgt, dass nicht die gesamte Seite neu geladen wird, wenn sich etwas ändert. D.h., wenn ein Button angeklickt wird.execute
stellt die Verbindung zu den <h:selectManyListbox>
-Elementen her. Das Format ist folgendes: FORM_ID:SELECT_MANY_LISTBOX_ID
.render
werden alle Elemente aufgeführt, die nach dem Klick auf den Button aktualisiert werden müssen.Mit diesen Informationen ist klar, wie der Reset-Button in der letzten Zeile funktioniert.
Damit der Warenkorb "ansprechender" aussieht, wurde mittels eines CSS-Stylesheets die Breite der Komponenten und die Farben angepaßt. Die CSS-Datei muss sich in einem Ordner css
im Verzeichnis resources
befinden und den Namen cart.css
haben, damit sie von der JSF-Seite gefunden wird.
TABLE.cartTable { width: 1000px; } .cartSelect { width: 400px; } .cartHeader{ text-align:center; font-size: 16px; font-weight: bold; } .cartRows{ text-align:center; background:none repeat scroll 0 0 #97ff83; border-top:1px solid #BBBBBB; }
Weitere Informationen zu Web-Clients findet man in den folgenden Artikeln: