World Wide Wicket 2.0

Durch die Verwendung von PropertyColumn kann direkt angegeben werden, welches Attribut aus der Domänenklasse in der aktuellen Spalte gerendert werden soll. Im folgenden Beispiel also beispielsweise der Tagespreis eines Films aus dem Attribut prizePerDay:

IColumn prizeColumn = new PropertyColumn(new Model("Preis / Tag"), "prizePerDay", "prizePerDay")

Die PropertyColumn erwartet im Konstruktor ein String-Modell für den Spaltentitel, eine optionale Expression für das Attribut, nach dem sortiert werden soll, sowie eine Expression für das Attribut, das angezeigt werden soll. Für einfache Attribute ist dies vollkommen ausreichend, in komplexeren Fällen (wie beispielsweise der Anzeige einer Liste von Schauspielern eines bestimmten Films) ist es meist notwendig, die Methode populateItem zu überschreiben:

populateItem(Item> item, String componentId, IModel rowModel)

In dieser Methode hat Herr K. die Möglichkeit, der aktuellen Zelle statt des standardmäßig gerenderten Labels eine eigene Wicket-Komponente hinzuzufügen. Leider sind die Möglichkeiten hier begrenzt, da man nur eine einzige Komponente zusammen mit der übergebenen componentId hinzufügen darf. Für den Fall der Darstellerliste reicht dies nicht aus. Herr K. behilft sich hier mit einem weiteren nützlichen Konzept des Wicket-Frameworks, den Fragmenten. Ein Fragment ist eine Inline-Komponente mit eigenem Markup. Das Fragment-Markup wird direkt im Template der umgebenden Komponente definiert (im aktuellen Fall also in SelectMoviePage.html):

Weiterhin definiert Herr K. die Fragmentklasse (nicht zwangsläufig) als innere Klasse in SelectMoviePage:

private static class ActorsFragment extends Fragment {
public ActorsFragment(String id, MarkupContainer markupProvider, IModel> model) {
super(id, "actorsListFragment", markupProvider, model);
add(new PropertyListView("actorsList", model){
protected void populateItem(ListItem item) {
item.add(new Label("name"));
}
});
}}

Ein Fragment erwartet außer seiner Komponenten-ID die Fragment-ID, über die das zugehörige Fragment-Markup identifiziert wird (es könnten ja durchaus mehrere Fragmente in einem Template definiert sein), den MarkupProvider (also die Klasse, die das Markup bereitstellt, in diesem Fall die SelectMoviePage selbst) sowie ein entsprechendes Modell.

In diesem Fall würde also jedes mal, wenn Herr K. das ActorsFragment verwendet, das entsprechende Fragment-Markup anhand der ID aus dem Template des übergebenen MarkupProviders geladen und die Liste der Namen der Hauptdarsteller ausgegeben werden:

public void populateItem(Item> item,
String componentId, IModel rowModel) {
item.add(new ActorsFragment(componentId,SelectMoviePage.this, new Model((Serializable)rowModel.getObject().getActors())));
}

Herr K. verwendet dieselbe Technik nochmals zum Rendern des BESTELLEN-Links, da eine PropertyColumn per Default nur ein Label rendert. Eine Link-Komponente benötigt jedoch zwingend ein <a/>-Tag im Markup. Dazu erzeugt Herr K. ein weiteres Fragment mit folgendem Markup:

Die Implementierung des Fragments entspricht prinzipiell der zuvor bereits besprochenen ActorsFragment-Klasse. Herr K. verwendet hier statt eines ListViews eine spezielle Link-Komponente. Diese betrachten wir ein wenig später genauer, wenn es um das Thema „Ajax“ geht.

Zuletzt fügt Herr K. noch den entsprechenden Tabellen-Header hinzu, in dem die entsprechenden Links für die Sortierung der Spalten angezeigt werden: dataTable.addTopToolbar(new HeadersToolbar(dataTable, movieDataProvider));.

Die Bestellung – ein wenig Dynamik mit Ajax

Herr K. ist bisher sehr zufrieden mit dem erreichten Ergebnis. Um seiner Anwendung ein wenig mehr Dynamik zu verleihen, fehlt seiner Ansicht nach noch ein wenig Ajax. Wicket beinhaltet bereits eine eigene Ajax Engine, sodass Herr K. keine Kenntnisse eines weiteren Frameworks benötigt (obwohl die Anbindung an Frameworks wie Dojo [6] problemlos möglich wäre).

Wicket und die Wicket Extensions bieten bereits eine Fülle an Ajax-Komponenten, die sich nahtlos in eine Anwendung integrieren lassen und für dynamisches Verhalten sorgen.

Eine kleine Anmerkung des Autors: Wicket und Ajax im Verbund bieten genügend Stoff für eine eigene Artikelreihe, sodass wir hier zusammen mit Herrn K. nur leicht an der Oberfläche kratzen können. Umfangreiche Beispiele zum Thema bietet aber z. B. [7].

Herr K. möchte, dass der BESTELLEN-Link eines Films nur dann angezeigt wird, wenn sich dieser nicht bereits im Warenkorb der Session befindet. Der User kann somit nur jeweils ein Exemplar eines Films bestellen. Natürlich wäre es schön, wenn das Ausblenden nicht jedes Mal ein Neuladen der Seite anstoßen würde. Ein Job für Ajax! Zunächst stöbert Herr K. ein wenig in den bereits vorhandenen Wicket-Komponenten und findet schließlich die AjaxLink-Komponente. Dies ist ein Link, der bei jedem Betätigen einen Ajax Request anstößt. Die Implementierung ist denkbar einfach und funktioniert, ohne dass sich Herr K. selbst mit dem notwendigen JavaScript befassen muss:

add(new AjaxLink("order", model){
public void onClick(AjaxRequestTarget target) {
VideoSession.get().getSelectedMovies().add(getModelObject());
target.addComponent(this);
  }
public boolean isVisible() {
Set selectedMovies = VideoSession.get().getSelectedMovies();
return !selectedMovies.contains(getModelObject());
}});

Genau wie die bereits zuvor verwendeten Link-Komponenten bietet ein AjaxLink eine onClick-Methode. Der Unterschied besteht darin, dass hier der Parameter AjaxRequestTarget übergeben wird. Das AjaxRequestTarget kann als Schnittstelle in die Wicket Ajax Engine betrachtet werden und bietet Methoden wie addComponent(Component c), appendJavascript(String script) oder prependJavascript(String script).

Mit der Methode appendJavascript()kann beispielsweise der JavaScript-Code zusammen mit einer Antwort zum Client gesendet werden, der nach dem Rendern ausgeführt wird. Mit der Methode addComponent()kann eine Komponente der Render Queue hinzugefügt werden und wird so erneut gerendert (mit ggf. geänderten Attributen wie der Sichtbarkeit). Hierfür ist es zwingend notwendig, dass für die Komponenten zuvor mit dem Aufruf von setOutputMarkupId(true) sichergestellt wird, dass sie ihre entsprechende Markup-ID rendern, damit sie im DOM des Browsers auch gefunden werden können.

Mit dem nun erworbenen Wissen fällt es Herrn K. leicht, den BESTELLEN-Button mit der entsprechenden Funktionalität zu implementieren. Die Implementierung der isVisible()-Methode stellt sicher, dass der Link nur dann angezeigt wird, wenn sich der zugehörige Film nicht bereits in der Session befindet. Damit der Link aber ausgeblendet wird, sobald der Film der Session hinzugefügt worden ist, muss die Komponente erneut gerendert werden. Dies geschieht, wie zuvor bereits diskutiert, über die Methode target.addComponent(this) .

Zuletzt bleibt noch die Implementierung der Bestätigungsseite. Diese beinhaltet keinerlei Logik und dient nur zur Anzeige des Bestätigungstextes. Die Umsetzung ist für Herrn K. mittlerweile ein Leichtes und wird hier im Artikel nicht näher erläutert.

Fazit

Herr K. hat eine funktionale Webanwendung entwickelt, über die sich prinzipiell schon einige wichtige Use Cases einer Onlinevideothek bedienen lassen (am Design könnte man eventuell noch ein wenig arbeiten). Leider reicht der Umfang eines Artikels bei Weitem nicht aus, den gesamten Funktionsumfang von Wicket vorzustellen. Trotzdem habe ich versucht, wichtige Themengebiete anzuschneiden und leider musste ich andere aus Platzgründen wegfallen lassen, wie beispielsweise hochinteressante Themen wie Behaviours, Internationalisierung und Unit Tests. Trotzdem hoffe ich, dass der Funke für das Wicket-Framework ein wenig auf Sie übergesprungen ist und der Artikel Lust macht, selbst ein wenig damit zu experimentieren. Das komplette Beispielprojekt steht unter [8] bereit. Gerne können Sie übrigens das Projekt auf einem eigenen GIT-Branch als Spielwiese nutzen und beliebig erweitern. Es würde mich freuen, wenn der eine oder andere Commit seinen Weg zurück ins Repository findet.

Damit verabschiede ich mich und hoffe, dass das Lesen des Artikels Ihnen genauso viel Spaß gemacht hat, wie mir das Schreiben. Und sollten Sie Herrn K. zufällig irgendwo einmal treffen, sprechen Sie ihn doch einfach auf seine Onlinevideothek an, eventuell. gibt es weitere spannende Geschichten aus der Wicket-Welt zu erzählen.

Martin Dilger arbeitet als Consultant bei der PENTASYS AG in München. Er beschäftigt sich beruflich hauptsächlich mit Git, Wicket, RCP, RAP und OSGi und bloggt regelmäßig unter http://splitshade.wordpress.com. Kontakt: martin.dilger@pentasys.de.
Kommentare

Schreibe einen Kommentar

Deine E-Mail-Adresse wird nicht veröffentlicht.