14.03.2012

Tapestry - Events aus Textfeldern / Default Actions definieren

Für komplexe Formulare ist nicht immer klar, wie diese auf das Drücken der Enter Taste reagieren. Per Default wird der erste Button im Formular ausgelöst. Dieses Verhalten ist aber nicht immer gewünscht, sondern soll nach Möglichkeit durch den Entwickler definiert werden können.

Leider bietet Tapestry von Haus aus keine Möglichkeit einen Default-Eventhandler zu registrieren. Mit dem Konzept der Mixins und der Nutzung der Prototype Bibliotheken ist eine Lösung trotz allem leicht zu realisieren.
Mixins bieieten die Möglichkeit bereits bestehende Komponenten mit zusätzlichem Verhalten auszustatten. Dies kann unter anderem zusätzliche Validierung, Autocompletion oder auch JavaScript Funktionalität sein. Eingeschränkt werden die Mixins nur durch die Tatsache, dass keine Templates genutzt werden. Die Templates müssen sich also in die Render-Phase integrieren.

Am einfachsten wird die Nutzung in dem bereits Eingangs beschriebene Szenario klar. Als Basis dient ein einfaches Tapestry-Projekt, welches durch Maven verwaltet wird. Die grundlegende Klassenstruktur gestaltet sich wie folgt:


Es existiert eine einzelne Seite, mit einem einfachen Eingabeformular für drei unterschiedliche Werte (value1, value2, value3). Zusätzlich beinhaltet das Formular noch drei Eventlinks, die durch das Betätigen der Enter-Taste innerhalb der Eingabefelder ausgelöst werden können.


Keep it simple, keep it clean ;-)

Der spannende Teil des Beispiels ist innerhalb des selbst defnierten Mixins, dort  wird die Erweiterung durch das entsprechende Prototype Snippet definiert und gerendert.


Das Mixin rendert die erstellte Prototype-Erweiterung während der Render-Phase und erweitert die entsprechende Komponente um die definierte Funktion. Die Prototype-Erweitung wird mit Hilfe der @Import Annotation importiert, zusätzlich, für die Simulation des Klicks, wird die Protolicius event.simulate.js inkludiert. Damit steht die Grundlage für die Definition der Prototype-Erweiterung:


In dieser wird definiert, dass auf dem keyup Event der Komponente die Funktion fireEvent ausgeführt wird. Somit kann innerhalb des Templates definiert werden, welches Event bei dem keyup Event der einzelnen Textfelder ausgeführt werden soll.


Die Events werden durch die Index Seite abgearbeitet.


Der Code findet sich, wie immer, im Repository.

11.03.2012

Chicken Wings mit Nacho "Auflauf"

Vorab, ein dickes Danke an M+K aus O für die Idee mit dem Nacho "Auflauf"! Total cool, wenngleich auch gegen jede Vernunft!

Wings und Nachos sind bei mir ein schnelles Gericht für den Abend, womit man nichts falsch machen kann. Kurze Marinierzeit, ergo schnelle Vorbereitung, und vor allem auch schnell fertig. Genau das richtige, wenn ich nach einer langen Woche nach Hause komme!

Grundlage bildet eine Schüssel Chickenwings, frisch vom MdV besorgt. Diese werden geputzt und danach abgetrocknet.


Danach die Flügelchen mit einem Rub der Wahl bestäuben und ein wenig marinieren lassen. Sicherlich kann man noch mit Öl oder ein wenig Honig dem Geschmack etwas auf die Sprünge helfen. Mir reicht es in diesem Fall allerdings nur mit Rub.


Nach 20-30 Minuten Marinierzeit gehen die Wings auf den Grill und es wird Zeit den "Auflauf" vorzubereiten. Grundzutaten:

  • Nachos
  • Salsa (im besten Fall selbstgemacht, wie hier)
  • Käse (hier ist es Cheddar geworden)
Sieht dann aus wie folgt:


Die Zubereitung ist simpel. Nachos in die Auflaufform, Salsa drüber und Käse oben druff!


Anschließend für 10-15 Minuten bei hoher Hitze in den Backofen oder, da der Grill ja eh an ist, mit auf den Grill. Sollte dann wie folgt ausschauen:


Yummi! Zeitgleich mit dem "Auflauf" sollten auch die Wings fertig sein, so dass diese vom Grill genommen werden können!


Fazit? Schnell gemacht, lecker und auf jeden Fall mehr als ausreichend. DAS gibt es sicherlich nochmal!

09.03.2012

Tapestry - Dynamische Formulare mit java.util.Map

Vielen Entwicklern wird es eventuell ähnlich gegangen sein, wie es mir gegangen ist. Irgendwo in der Anwendung findet sich ein Zugriff auf die Datenbank mit Hilfe eines JdbcTemplates und die Ergebnisse werden durch eine Liste von Maps repräsentiert.


Die Anzeige mag noch einfach sein, dort kann einfach eine Grid Komponente genutzt werden. Die Bearbeitung eines einzelnen Datensatzes ist spätestens dann nicht mehr!
Aus diesem Grund möchte ich die Arbeit mit einer Map innerhalb eines Formulars vorstellen. Demonstriert wird dies mit Hilfe eines einfachen Tapestry Projektes, welches mittels Maven verwaltet wird. Das Grundgerüst des Projektes entspricht dem Standard Tapestry Projekt. Der generelle Projekt-Aufbau kann in der Dokumentation nachgelesen werden.


Im wesentlichen besteht das Projekt aus einer einzelnen Seite, einer Service-Implementierung unddem Anwendungsmodul, in dem der Service registriert wird. Das folgende Klassendiagramm veranschaulicht die Beziehungen unter den Klassen.


Wie bereits beschrieben, besteht das Projekt aus dem Service inkl. seiner Implementierung (DataService und DataServiceMock), dem Anwendungsmodul für die Registrierung der Services (AppModule) und der eigentlichen Visualisierung inkl. Controller (Index.tml und Index).

Zusätzlich finden sich noch weitere Konfigurationsdateien (log4j.xml) und Maven-Build-Files (build.properties und pom.xml) in dem Projekt, auf diese werde ich an dieser Stelle nicht näher eingehen.

Gestartet wird die Anwendung über das Jetty-Plugin, welches in der pom.xml definiert ist.


Damit kann das Projekt nach dem Auschecken direkt gestartet werden, es ist keine weitere Konfiguration mehr notwendig.

Die Anwendung an sich ist so einfach gehalten, dass der eigentliche Sinn des Beispiels nicht untergeht. Das folgende Sequenzdiagramm beschreibt den Ablauf des einfachen Workflows.



Der Nutzer ruft also die Seite auf, wählt die ID der zu bearbeitenden Zeile, führt die Bearbeitung der Werte durch und die Zeile wird mit dem Submit des Formulars in der Session aktualisiert. Um zu verstehen, wie das Formular dynamisch aufgebaut und gefüllt wird, werden die einzelnen Schritte sukzessive betrachtet.

1. Auswahl der ID der zu bearbeitenden Zeile

Die vorhandenen IDs werden direkt aus dem DataService geholt. Die Implementierung hält die Werte nach der Initialisierung der Klasse innerhalb der Session vor.


Innerhalb der Seite Index wird der Dienst mit Hilfe der @Inject Annotation durch Tapestry injiziert. Dort wird dann auf die Liste der vorhandenen IDs zurückgegriffen.


Die Liste der IDs wird wiederum im Template zur Visualisierung der Select Komponente genutzt.


Die Komponente ist natürlich in das gesamtheitliche Template eingebettet und liegt innerhalb eines Formulars, welches durch den Nutzer abgeschickt werden kann. Für den Nutzer bietet sich also beim ersten Betrachten der Seite das folgende Bild:


Nach dem Wählen der ID zur Bearbeitung einer einzelnen Spalte wird das Formular abgesendet und mit dem zweiten Schritt und dem eigentlichen dynamischen Aufbau des Formulars begonnen.

2. Laden der zu bearbeitenden Zeile und Aufbau des Formulars


Nach der Auswahl der ID wird die Zeile durch die Seite geladen und der dynamische Aufbau des Formulars kann beginnen. Die durch den Nutzer gewählte Zeile wird dabei in der Variablen currentRow in der Session vorgehalten. Die Verknüpfung zum Template wird durch die @Property Annotation erreicht, welche einen direkten Zugriff auf die Variable ohne Getter und Setter ermöglicht.




Für die Visualisierung der Felder wird über die Einträge der aktuellen Zeile iteriert. Jeder Eintrag innerhalb der Map bekommt ein eigenes Eingabefeld, welches dem Nutzer zur Bearbeitung zur Verfügung steht.




Das Iterieren wird mit Hilfe der Loop Komponente durchgeführt. Diese bietet die Möglichkeit über eine Menge von Objekte zu iterieren und diese dann mit Hilfe von weiteren Komponenten zu verarbeiten. Das aktuelle Objekt wird hierbei innerhalb der Index Seite gespeichert.


Für die spätere Verarbeitung der einzelnen Felder ist dies ein wesentlicher Punkt. Dieses Attribut wird bei der Aktualisierung der Werte noch eine weitere Rolle spielen. Damit der State des Forms wieder hergestellt werden kann, wird dem Formular ein sogenannter ValueEncoder mitgegeben. Ist für Tapestry nicht ersichtlich, wie die im Formular gespeicherten Werte in eine eindeutige String-Darstellung und zurück gewandelt werden können, muss ein solcher ValueEncoder definiert werden. Für einfache Datentypen sind bereits ValueEncoder definiert und müssen somit nicht erneut erstellt werden.


Im zweiten Teile des Templates ist zu sehen, dass für jeden Eintrag innerhalb der Map ein TextField generiert wird. Der einzige Unterschied zur Standardnutzung ist an dieser Stelle der Zugriff auf die Daten, die durch das TextField verwaltet werden. Zu sehen ist, dass nicht direkt auf die Rendervariable der Schleife zurückgegriffen wird, sondern auf ein Attribut der Seite an sich.


Das entsprechende Feld, bzw. die Zugriffsmethoden, sind in der Index Seite definiert und greifen dort direkt auf den Eintrag in der Map zurück.


Damit werden die vom Nutzer erfassten Werte direkt in der Map bzw. dem Eintrag in der Map gespeichert. Für den Nutzer stellt sich die Bearbeitung der Zeile wie ein normales Formular dar:


Somit steht der Verarbeitung bzw. Bearbeitung von Einträgen innerhalb einer Map nichts mehr im Wege. Die Daten können leicht aus dem Formular übertragen und in die Map überführt werden. Denkbar ist auch eine andere Verwertung der Daten, da diese nicht zwingend an die Map gebunden sind.

Der gesamte Code ist wie immer im Repository zu finden.

05.03.2012

NWAEV - Jahreskarten für den LFV Westfalen und Lippe e.V.

Sicherlich für den einen oder anderen uninteressant, aber für viele eine hilfreiche Information, Karten für den LFV Westfalen und Lippe können über den NWAEV bezogen werden.

Da die Geschäftsstelle des NWAEV nur sehr kurze Öffnungszeiten hat und zu diesen die Mitarbeiter meist keine Zeit haben auf Mails zu antworten nun an dieser Stelle diese interessante Information.

Die Jahreskarte für den LFV Westfalen und Lippe kann über die Geschäftsstelle der NWA bezogen werden. Kostenpunkt für das Jahr 2012 sind insgesamt 20€. Dafür erhält man die Berechtigung die folgenden Gewässer zu befischen:
Ich hoffe, dass diese Info dem einen oder anderen die aufwändige Suche erspart!