Praktische Einführung in die Datenhaltung von Vaadin

Seite 3: Lasset es gridden

Inhaltsverzeichnis

Nach diesem rudimentären ersten Gehversuch nimmt sich der Autor einer komplexeren Aufgabe an. Ganz im Sinne des besprochenen Datenmodells ist nun das Realisieren einer Tabelle mit Kursinformationen dran – eine Aufgabe, die für gewöhnlich unter Nutzung der Modell-Engine zu bewältigen ist.

Los geht es mit einer Tabellenstruktur, die in der EnterView implantiert wird:

public void enter(ViewChangeEvent event) { . . .
Table table = new Table("Informationen über Edelmetalle");
table.addContainerProperty("Name", String.class, null);
table.addContainerProperty("Kurs", Float.class, null);
table.setWidth("80%");
addComponent(table);
}

Zu diesem Zeitpunkt würde die Tabelle im Formular erscheinen. Dank des Aufrufs von setWidth ist zudem sichergestellt, dass sie ausreichend Platz am Bildschirm einnimmt. Rein theoretisch könnten Entwickler nun den im Tabellen-Widget befindlichen Datenspeicher befüllen. Das Beispiel soll einen Schritt weitergehen. Für die Anlieferung der Informationen deployen Entwickler einen BeanContainer, der als Member der EnterView entsteht:

public class EnterView extends VerticalLayout implements View, ValueChangeListener {
BeanContainer<String, HeiseBean> myCollection = new BeanContainer<String, HeiseBean>(HeiseBean.class);

Der BeanContainer von Vaadin ist auf das Verarbeiten klassischer JavaBeans optimiert. Legt man eine neue Klasse namens HeiseBean an, ist folgende Struktur zu sehen:

public class HeiseBean implements Serializable {
private String myName;
private Float myPrice;
public HeiseBean(String _n, Float _p){
myName=_n;
myPrice=_p;
}
public String getMyName() {
return myName;
}
public void setMyName(String myName) {
this.myName = myName;
}
...

Aus Platzgründen werden Setter und Getter nicht gezeigt: Weitere Informationen finden sich in diversen Tutorials zum Thema Java Beans. Die nächste Amtshandlung ist das Einpflegen der Beans in den Container, was durch folgende Struktur erfolgt:

public void enter(ViewChangeEvent event) {
...
myCollection.setBeanIdProperty("myName");
myCollection.addBean(new HeiseBean("Gold", (float) 1866.0));
myCollection.addBean(new HeiseBean("Silber", (float) 30.0));
myCollection.addBean(new HeiseBean("Platin", (float) 2100.0));

Von besonderer Relevanz ist die Methode setBeanIdProperty, die den Namen des als Primärschlüssels agierenden Werts der Bean-Klasse entgegennimmt. Vaadin arbeitet an der Stelle mit klassischer Reflektion, weshalb korrekte Groß- und Kleinschreibung wichtig ist. Zu beachten ist zudem, dass der zu übergebende String den Variablennamen und nicht den der Property darstellt (hier myName). Danach folgen einige Aufrufe von addBean, die neu angelegte Bean-Instanzen in den Speicher einschreiben.

Als nächste Aufgabe ist der Speicher mit der Tabelle zu verdrahten:

public void enter(ViewChangeEvent event) {
...
table.setContainerDataSource(myCollection);
addComponent(table);

Damit ist diese Version des Programms einsatzbereit – es funktioniert in so gut wie allen Browsern problemlos.

Das direkte Anzeigen von Bean-Informationen mag aus akademischen Gründen interessant sein, einen wirklichen Vorteil gegenüber dem direkten Einschreiben der Informationen in das Array erhält man indes erst, wenn die Fähigkeiten des Data-Binding-Subsystems zum Einsatz kommen.

Zur bestmöglichen Beschreibung der zur Verfügung stehenden Funktionen zeigt Abbildung 2 die Interfaces, die im Zusammenspiel mit Containern zur Lösung von Problemen bei der Datenanzeige eingesetzt werden.

Container können über diverse Interfaces Hilfsleistungen von Drittklassen anfordern (Abb. 2)

(Bild: Vaadin)

Der BeanContainer implementiert das Interface Container.Filterable, weshalb die Realisierung einer Sortierung mit geringem Aufwand von der Hand geht. Vaadin wird von Haus aus mit zwei Filtergruppen ausgeliefert: Atomic Filter verarbeiten Werte, während Composite Filter auf das Verdrahten mehrerer Filter spezialisiert sind.

Los geht es damit, einen Composite Filter zur Verdrahtung zweier Atomic Filter einzuspannen. Aus didaktischen Gründen beginnt der Autor mit dem Erzeugen der einzelnen Filterinstanzen:

SimpleStringFilter stringFilter = new SimpleStringFilter("myName", "Gold", true, false);
Compare compareFilter=new Compare.Greater("myPrice", (float)2000);

SimpleStringFilter vergleicht die Werte einer bestimmten Member-Variable mit einem vom Entwickler zu spezifizierenden Parameter. Die beiden booleschen Parameter erlauben das Beeinflussen des Vergleichsverfahrens: Neben dem Ignorieren von Groß- und Kleinschreibung kann man einen präfixbasierten Vergleich aktivieren.

Über das Compare-Objekt bietet Vaadin eine Gruppe von Methoden an, die auf numerische Vergleiche optimiert sind. Auch hier erfolgt die Initialisierung durch Einreichen eines Objektnamens und eines Vergleichswerts. Hier ist zu beachten, dass Compare selbst keine Casts vornimmt – wer das (float) vor dem numerischen Wert weglässt und ein Integer übergibt, bekommt eine Fehlermeldung.

Da Container nur ein Filterobjekt entgegennehmen können, ist das Erzeugen eines Kompositfilters erforderlich. Seine Aufgabe ist das Zusammenfassen der von den beiden Filterklassen erzeugten Parameter:

Filter combineFilter=new Or(stringFilter, compareFilter);

Neben dem hier verwendeten Or-Filter gibt es eine Implementierung von And und eine boolsche Not-Operation, die einen einzelnen Wert invertiert. Im nächsten Schritt wird der Kompositfilter beim Container angemeldet, um die Darstellung zu beeinflussen:

myCollection.addContainerFilter(combineFilter);

Damit sind die Änderungen abgeschlossen. Nach dem Öffnen der Webapplikation kann man sich an der reduzierten Auswahl an Metallen erfreuen.

Entwickler sind in Vaadin nicht auf das "primitive" Verbinden vorhandener Filter beschränkt. Der Interface-Filter erlaubt das Realisieren von mehr oder weniger beliebigen Filterstrukturen. Seine Deklaration sieht in Vaadin so aus:

import com.vaadin.data.Container.Filter;
public class HeiseFilter implements Filter{
@Override
public boolean passesFilter(Object itemId, Item item) throws UnsupportedOperationException {}
@Override
public boolean appliesToProperty(Object propertyId) { }
}

Aus technischer Sicht findet sich hier nichts Außergewöhnliches: Der Filter wird auf die diversen Elemente angewendet und gibt sodann einen booleschen Wert zurück, der das Framework über die Ergebnisse seiner Arbeit informiert. Eine kleine Falle ist das Inkludieren der korrekten Version von Filtern – Java bringt mehrere gleichnamige Klassen mit, die mit dem Vaadin-Filter nichts am Hut haben.

AppliesToProperty informiert die Engine, ob ein Filter mit einer bestimmten Eigenschaft etwas anzufangen weiß. Bei kleineren Filteraufgaben reicht es aus, für alle Aufrufe "true" zurückzugeben und die Performance-Penalty zu "fressen". Aus didaktischen Gründen nutzt der Autor stattdessen folgende Version, die das Entpacken der angelieferten Objekte demonstriert:

@Override
public boolean appliesToProperty(Object propertyId) {
return propertyId != null && ( propertyId.equals("myName") || propertyId.equals("myPrice") );
}

Aufgabe Numero zwei ist der eigentliche Vergleich der vom Framework angelieferten Informationen. Das geschieht in passesFilter:

@Override
public boolean passesFilter(Object itemId, Item item) throws UnsupportedOperationException {
Property p = item.getItemProperty("myName");
String value = (String) p.getValue();
p = item.getItemProperty("myPrice");
Float fVal = (float) p.getValue();
if(fVal<50 && value.compareTo("Silber")==0)
return true;
return false;
}

Die Methode ist für alle Objektabfragen des Filters gleichermaßen verantwortlich: Entwickler müssen also damit beginnen, die diversen Felder auseinanderzubringen. Danach folgt der eigentliche Vergleich gegen die jeweiligen Kriterien, dessen Realisierung allein im Ermessen der Entwickler liegt.

Damit fehlt nur noch die Integration zwischen Container und Filter. Das wird abermals in EnterView realisiert. Dafür müssen Entwickler addContainerFilter einen Verweis auf eine Instanz von HeiseFilter übergeben. An der Stelle können sie das Programm erneut ausführen: Die Tabelle besteht nun nur noch aus dem Eintrag für Silber, weil die anderen beiden Metalle dem Filter zum Opfer fallen.