Tabele na stronach aplikacji JSF
W artykule "Tabele o dynamicznej ilości kolumn w aplikacji JSF" opisałem, w jaki sposób prezentować na stronach JSF tabele o nieustalonej ilości kolumn z użyciem komponentów dataTable i columns z biblioteki Tomahawk. Miał to być jeden z elementów większej układanki, której ostatecznym celem jest zbudowanie komponentu JSF – w formie kompozycji Facelets – adresującego problem, o którym pisałem z kolej w artykule "Prezentacja dużych tabel na stronach HTML". A problemem jest to, że – jak tam napisałem – "chciałbym mieć komponent, który umożliwi prezentację dużej tabeli na stronie HTML, przy czym tabela ta, jest zarówno bardzo długa, jak i bardzo szeroka". Dobrą wiadomością jest, że problem jest już niemalże rozwiązany, tzn. komponent ten powstał i działa niemalże tak jakbym sobie tego życzył, tyle że nie dało się go zaimplementować stosując podejście z użyciem wspomnianych komponentów Tomahawk. W sukurs przyszedł inny komponent, również z biblioteki Tomahawk, a mianowicie dataList. Dzisiaj napiszę więc o innym podejściu do implementacji tabel "o dynamicznej ilości kolumn w aplikacji JSF". Podejściu bazującym na komponencie dataList.
Zacznijmy od modelu danych. Żeby nie utrudniać, załóżmy że nagłówki – tj. nagłówek górny i boczny – oraz treść prezentowana w tabeli to będą zwykłe napisy, a więc nasz model danych może się opierać na obiektach klasy String. Poniżej interfejsy oraz przykładowe implementacje modelu danych potrzebnego dla naszej tabeli.
Zacznijmy od modelu danych. Żeby nie utrudniać, załóżmy że nagłówki – tj. nagłówek górny i boczny – oraz treść prezentowana w tabeli to będą zwykłe napisy, a więc nasz model danych może się opierać na obiektach klasy String. Poniżej interfejsy oraz przykładowe implementacje modelu danych potrzebnego dla naszej tabeli.
public interface TableDataModel {
public List<String> getColsHeaders();
public List<RowDataModel> getRowsDataModel();
}
public interface RowDataModel {
public String getRowHeader();
public List<String> getCellsList();
}
public class BasicTableDataModel implements TableDataModel {
private List<String> colsHeaders;
private List<RowDataModel> rowsDataModel;
public BasicTableDataModel(String[] colsHeaders) {
this.colsHeaders =
new ArrayList<String>(Arrays.asList(colsHeaders));
}
public List<String> getColsHeaders() {
return colsHeaders;
}
public List<RowDataModel> getRowsDataModel() {
if (rowsDataModel == null)
rowsDataModel = new ArrayList<RowDataModel>();
return rowsDataModel;
}
public void addRow(RowDataModel rowDataModel) {
getRowsDataModel().add(rowDataModel);
}
}
public class BasicRowDataModel implements RowDataModel {
private String rowName;
private List<String> cellsList;
public BasicRowDataModel(String rowName, String[] cellsArray) {
this.rowName = rowName;
this.cellsList =
new ArrayList<String>(Arrays.asList(cellsArray));
}
public List<String> getCellsList() {
return cellsList;
}
public String getRowHeader() {
return rowName;
}
}
Implementacje modelu danych pokazane powyżej zostały skonstruowane pod kątem łatwości użycia w kodzie przykładowym, więc ziarno zarządzane JSF, którego użyjemy do budowy strony jest niezwykle proste. Oto jego kod, w całości poświęcony inicjalizacji danych:
public class TableBackingBean {
private BasicTableDataModel dataModel;
public BasicTableDataModel getDataModel() {
if (dataModel == null) {
dataModel = new BasicTableDataModel(
new String[] { "Ford", "Opel", "BMW" });
dataModel.addRow(new BasicRowDataModel(
"2005", new String[] {"12", "8", "10" }));
dataModel.addRow(new BasicRowDataModel(
"2006", new String[] {"-6", "2", "12" }));
dataModel.addRow(new BasicRowDataModel(
"2007", new String[] {"16", "18", "-2" }));
}
return dataModel;
}
}
O komponencie dataList możemy myśleć, nieco upraszczając, jak o implementacji pętli "for each". Użyjemy go więc jako iteratora po elementach kolekcji modelu danych i w ten sposób zbudujemy tabelę HTML. Oto, jak wygląda nasza strona:
<table>
<tr>
<td> </td>
<t:dataList var="headerCell"
value="#{tableBackingBean.dataModel.colsHeaders}">
<td>#{headerCell}</td>
</t:dataList>
</tr>
<t:dataList var="bodyRow"
value="#{tableBackingBean.dataModel.rowsDataModel}">
<tr>
<td>#{bodyRow.rowHeader}</td>
<t:dataList var="cell" value="#{bodyRow.cellsList}">
<td>#{cell} %</td>
</t:dataList>
</tr>
</t:dataList>
</table>
Ziarno zarządzane o nazwie tableBackingBean to naturalnie instancja klasy TableBackingBean. Efektem końcowym jest tabela prezentująca procentowy wzrost, lub spadek, ilości sprzedawanych aut przez poszczególnych producentach w poszczególnych latach.