Wicket 6.0 – Teil 2 – Validierung von Forms

Und weiter gehts, im letzten Teil haben wir uns grundsätzlich über die neue Backing-Library Funktionalität von Wicket 6 unterhalten und die überfällige JQuery-Integration.

Heute schauen wir mal, ob und wenn ja welche Neuigkeiten Wicket 6 für die Validierung mitbringt.

Hierfür bauen wir eine sehr einfache Form, über die man ein Kunde seine E-Mailadresse ändern kann.




<meta charset="utf-8" />
Apache Wicket Quickstart
	<link title="Stylesheet" href="style.css" rel="stylesheet" media="screen" type="text/css" /></pre>
<form>
<input type="text" />
<input type="submit" /></form>
<pre>


Der Java-Code sieht so aus:


public class HomePage extends WebPage {
private static final long serialVersionUID = 1L;

public HomePage(final PageParameters parameters) {
Form form = new Form("form", new CompoundPropertyModel(new Email()));
form.add(new TextField("mail"));
form.add(new AjaxSubmitLink("submit") {
@Override
protected void onSubmit(AjaxRequestTarget target, Form<?> form) {
System.out.println(form.getDefaultModelObject());
target.add(HomePage.this);
}

@Override
protected void onError(AjaxRequestTarget target, Form<?> form) {
target.add(HomePage.this);
}
});
add(form);
}
}

Zusätzlich bauen wir uns noch eine einfache Email-Klasse:


/**
* @author martin.dilger
*/
public class Email implements Serializable {

private String mail;

public Email(){}

public Email(String email){
this.mail = email;
}

public String getMail(){
return mail;
}

@Override
public String toString() {
return mail;
}
}

Die Form wird über einen einfache Ajax-Submitlink abgeschickt. Bisher nichts neues, der Code sieht mit Wicket1.5.x  genauso aus.

Schickt man die Form ab, passiert erst mal nichts. Was uns noch fehlt ist die ganze Logik zum Validieren der E-Mailadresse.

Hierfür extrahieren wir die Erzeugung des Email-Textfeldes in eine eigene Factory-Methode:


private TextField emailField() {
TextField mail = new TextField("mail");

Das Model der Form ist bereits ein CompoundPropertyModel vom Typ Email, es reicht also, wenn das Textfeld mit der ID „mail“ instantiiert wird.

Da wir für die Validierung eine Instanz unserer Email benötigen, braucht das Textfeld zunächst mal einen Konverter (ansonsten könnten wir nur auf einem String arbeiten).


TextField mail = new TextField("mail"){
@Override
public  IConverter getConverter(Class type) {
return (IConverter)new AbstractConverter(){
@Override
protected Class getTargetType() {
return Email.class;
}

@Override
public Email convertToObject(String s, Locale locale) {
return new Email(s);
}

@Override
public String convertToString(Email value, Locale locale) {
return value.getMail();
}
};
}
};

Auch das unterscheidet sich in keiner Weise von Wicket 1.5.x.

Was wir jetzt aber machen können ist, einen Validator an unsere Komponente zu hängen (ja ich weiß, es gibt einen fertigen EmailAddressValidator..)


mail.add(new IValidator() {
@Override
public void validate(IValidatable validatable) {
Email email = validatable.getValue();
if (!email.isValid()) {
ValidationError validationError = new ValidationError().addKey("email.error");
validationError.setVariable("email", email.getMail());
validatable.error(validationError);
}
}
});

Die Klasse AbstractValidator ist mittlerweile deprecated, wir arbeiten also direkt auf IValidator.

Zunächst erzeugen wir eine neue ValidationError-Instanz. ValidationError selbst ist ziemlich mächtig, beispielsweise lädt dieser selbstständig Fehlermeldungen über den registrieren Fehler-Key.


new ValidationError().addKey("email.error");

Leider, leider gibt es in der aktuellen Version (Wicket-6.0-beta1) einen Bug, der eine NullPointerException nach sich zieht, wenn hier ein Propertykey angegeben wird, der nicht vorhanden ist. Dieser Bug ist aber mittlerweile gefixt und sollte mit der Wicket-6.0-beta2 ausgerollt werden (die sollte eigentlich heute abend erscheinen).

Zusätzlich hat man die Möglichkeit, Variablen zu registrieren.

validationError.setVariable("email", email.getMail());

Man kann dann beispielsweise eine solche Fehlermeldung in einer Propertydatei definieren:

email.error=Ungueltige Emailadresse : ${email}

Der Platzhalter email würde durch die Variable ersetzt werden, was in diesem Fall die eingegebene Emailadresse ist.

FeedbackMessages

Eine interessante Neuigkeit hat sich mit Wicket-6 aber doch noch fast unbemerkt eingeschlichen.
Feedback-Messages (beispielsweise nach einem Konvertierungs- oder Validierungsfehler) wurden bisher immer in der Session gespeichert. Das hat sich grundlegend geändert, denn in Wicket 6 merkt sich jede Komponente selbst, welche Fehler für sie vorhangen sind.

Wie funktioniert das im Code?

public FeedbackMessages getFeedbackMessages()
	{
		FeedbackMessages messages = getMetaData(FEEDBACK_KEY);
		if (messages == null)
		{
			messages = new FeedbackMessages();
			setMetaData(FEEDBACK_KEY, messages);
		}
		return messages;
	}

Hierfür hält jede Komponente eine Instanz der Klasse FeedbackMessages.

Wie kam man bisher an alle registrierten FeedbackMessages?


FeedbackMessages messages = Session.get().getFeedbackMessages()

Das funktioniert immer noch, würde aber in Wicket-6 nur die Meldungen zurückliefern, die explizit in der Session gespeichert wurden (was beispielsweise dann Sinn macht, wenn Fehlermeldungen über Page-Grenzen hinweg erhalten bleiben sollen).

Wie aber kommt man jetzt an alle Fehlermeldungen, die beispielsweise beim Abschicken einer Form erzeugt wurden?

Hierfür gibt es einen neuen Mechanismus, den FeedbackMessageCollector.

Schauen wir uns das Ganze im Beispiel an:

Wir haben ja bereits ein FeedbackPanel auf der einfachen Seite eingebaut, das einen Fehler anzeigt, wenn eine falsche Emailadresse eingegeben wird.

Wieso funktioniert das? Das zeigt ein einfacher Blick in die neue Implementierung des FeedbackPanels, genauer sogar in der Klasse FeedbackMessagesModel.

messages = new FeedbackCollector(pageResolvingComponent.getPage()).collect(filter);

Was passiert intern? Der Collector sucht rekursiv in der übergebenen Komponente (hier die Page) und allen Kind-Komponenten
(und optional auch der Session) nach Meldungen, die registriert sind und zeigt diese an.

Mir gefällt dieser Ansatz ganz gut, da jetzt die Zuständigkeit für das Verwalten von FeedbackMessages von der Session
(die eigentlich mit dieser Thematik überhaupt nichts zu tun haben sollte) in die Komponenten gewandert ist
(wo sie auch hingehört).

Vielleicht noch ein letztes Wort zum Cleanup von FeedbackMessages.

Am Ende jedes Requests werden FeedbackMessages weggeräumt. Die geschieht in der detachFeedback-Methode der Klasse
Component:

private void detachFeedback()
	{
		FeedbackMessages feedback = getMetaData(FEEDBACK_KEY);
		if (feedback != null)
		{
			final int removed = feedback.clear(getApplication().getApplicationSettings()
				.getFeedbackMessageCleanupFilter());

			if (feedback.isEmpty())
			{
				setMetaData(FEEDBACK_KEY, null);
			}
			else
			{
				feedback.detach();
			}
		}
	}

Recht interessant ist hierbei noch folgende Zeile:

feedback.clear(getApplication().getApplicationSettings()
				.getFeedbackMessageCleanupFilter())

Der FeedbackMessage-Cleanup-Filter entfernt per Default alle Meldungen, die bereits gerendert wurden.
Das passiert per Default auch in der Session.

Es funktioniert aber weiterhin problemlos, eine FeedbackMessage auf Seite A zu registrieren:

Session.get().error("Fehler!");
setResponsePage(SeiteB.class);

und diesen Fehler auf Seite B anzuzeigen, da in diesem Fall die FeedbackMessages noch nicht gerendert wurde und somit
auch nicht am Ende des Requests in der detach()-Phase weggeräumt werden.

Im nächsten Artikel schauen wir uns dann ein wenig Ajax-Neuheiten genauer an.

Die Sourcen zu allen Artikeln kann man sich übrigens hier klonen : git@github.com:dilgerma/wicket-6.0-Playground.git

@Override
public final List<FeedbackMessage> getObject()
{
if (messages == null)
{
// Get filtered messages from page where component lives
messages = new FeedbackCollector(pageResolvingComponent.getPage()).collect(filter);

// Sort the list before returning it
if (sortingComparator != null)
{
Collections.sort(messages, sortingComparator);
}

// Let subclass do any extra processing it wants to on the messages.
// It may want to do something special, such as removing a given
// message under some special condition or perhaps eliminate
// duplicate messages. It could even add a message under certain
// conditions.
messages = processMessages(messages);
}
return messages;
}

Der nächste Teil der Wicket-6 Serie ist hier zu finden.


War dieser Blogeintrag für Sie interessant? Evtl. kann ich noch mehr für Sie tun.

Trainings & Know-How aus der Praxis zu

  • Apache Wicket 1.4.x, 1.5.x, 1.6.x
  • GIT – Best Practices, Einsatz, Methoden
  • Spring
  • Java
  • Scrum & Kanban
  • Agiles Arbeiten
Consulting & Softwareentwicklung

  • Requirements Engineering
  • Qualitätssicherung
  • Software-Entwicklung
  • Architektur
  • Scrum & Kanban
Advertisements

2 Gedanken zu „Wicket 6.0 – Teil 2 – Validierung von Forms

  1. Pingback: Wicket in meinem MoinMoin Wiki | Hotchpotch Blog

  2. Pingback: Wicket 6 und JQuery – Dream Team in Action | Softwareentwicklung und Agiles Arbeiten

Kommentar verfassen

Trage deine Daten unten ein oder klicke ein Icon um dich einzuloggen:

WordPress.com-Logo

Du kommentierst mit Deinem WordPress.com-Konto. Abmelden / Ändern )

Twitter-Bild

Du kommentierst mit Deinem Twitter-Konto. Abmelden / Ändern )

Facebook-Foto

Du kommentierst mit Deinem Facebook-Konto. Abmelden / Ändern )

Google+ Foto

Du kommentierst mit Deinem Google+-Konto. Abmelden / Ändern )

Verbinde mit %s