14 stycznia 2008

Tabele o dynamicznej ilości kolumn w aplikacji JSF

W większości przypadków tabele mają z góry określoną liczbę, znaczenie i nazwy kolumn a zmienna jest liczba wierszy. Kolumny tabeli określają wtedy nijako typ obiektu a wiersze to poszczególne obiekty zgodne z tym typem. Nie wszystkie tabele mają jednak tą naturę. Czasem nieznana jest także liczba kolumn, np. w przypadku, gdy w tabeli chcemy pokazać stopę bezrobocia w wybranych krajach i wybranych okresach, latach albo miesiącach. Dziś pokażę, jak można zaimplementować taką tabelę w aplikacji JSF z użyciem komponentów dataTable i columns z biblioteki Tomahawk. Zacznijmy od końca, czyli jak będzie wyglądało to, co zbudujemy.


A teraz kod źródłowy strony. Zmienna o nazwie dataBean to ziarno zarządzane klasy DataBean, której kod pokaże później.

<t:dataTable var="period" value="#{dataBean.periodsList}">
<t:column>
<h:outputText value="#{period.name}" />
</t:column>

<t:columns value="#{dataBean.countriesList}" var="country">
<f:facet name="header">
<h:outputText value="#{country.name}" />
</f:facet>

<h:outputText value="#{dataBean.aggregateData.unemploymentRate} %" />
</t:columns>
</t:dataTable>

Kluczem do zrozumienia tego mechanizmu jest klasa javax.faces.model.DataModel. Wyrażenia dataBean.periodsList oraz dataBean.countriesList są właśnie tego typu, co widać w kodzie wspomnianej klasy DataBean. Poniżej jej kod źródłowy.

import java.util.ArrayList;

import javax.faces.model.DataModel;
import javax.faces.model.ListDataModel;

public class DataBean {
private DataModel periodsList;

private DataModel countriesList;

public DataModel getPeriodsList() {
if (periodsList == null)
periodsList = new ListDataModel(fetchPeriodsList());

return periodsList;
}

public DataModel getCountriesList() {
if (countriesList == null)
countriesList = new ListDataModel(fetchCountriesList());

return countriesList;
}

public AggregateData getAggregateData() {
if (getPeriodsList().isRowAvailable() && getCountriesList().isRowAvailable()) {
return getData(
(Country) getCountriesList().getRowData(),
(Period) getPeriodsList().getRowData());
}

return null;
}

private ArrayList<Period> fetchPeriodsList() {
ArrayList<Period> periods = new ArrayList<Period>();

periods.add(new Period("Styczen 2007"));
periods.add(new Period("Luty 2007"));
periods.add(new Period("Marzec 2007"));

return periods;
}

private ArrayList<Country> fetchCountriesList() {
ArrayList<Country> countries = new ArrayList<Country>();

countries.add(new Country("Polska"));
countries.add(new Country("Litwa"));
countries.add(new Country("Rumunia"));

return countries;
}

private AggregateData getData(Country country, Period period) {
return new AggregateData(
country.getName().length() + period.getName().length());
}
}

Działa to więc tak, że dostarczamy kolekcje (typu javax.faces.model.DataModel) obiektów dla nagłówków poziomych i pionowych (metody getPeriodsList() i getCountriesList()) oraz implementujemy metodę (w tym wypadku jest to getAggregateData()), która dostarcza danych dla poszczególnych komórek "ciała" tabeli. Używając metody getRowData() klasy javax.faces.model.DataModel dowiadujemy się o obiektach będących w nagłówkach dla bieżącej komórki tabeli i na tej podstawie pobieramy odpowiednie dane. Zwróćmy uwagę, że pobranie danych oddelegowałem do metody getData(Country country, Period period). To jak pobieramy dane nie jest meritum tego artykułu, więc umieściłem tam kod-zaślepkę, ale w normalnym przypadku było by tu zapewne wywołanie jakiejś metody odpowiedniego serwisu logiki biznesowej, która z kolei pobierałaby dane z bazy danych. Dla kompletności podam jeszcze kod źródłowy klasy AggregateData. Analogicznie wyglądają klasy Country i Period. Są to ziarna JavaBeans z jednym atrybutem o nazwie name i konstruktorem.

public class AggregateData {
private int unemploymentRate;

public AggregateData(int unemploymentRate) {
this.unemploymentRate = unemploymentRate;
}

public int getUnemploymentRate() {
return unemploymentRate;
}

public void setUnemploymentRate(int unemploymentRate) {
this.unemploymentRate = unemploymentRate;
}
}

Brak komentarzy: