您好,欢迎来到钮旅网。
搜索
您的当前位置:首页Using datatables

Using datatables

来源:钮旅网
Using datatables

Fast menu Introduction

This whole JSF datatable howto is based on the use of a backing bean with the request scope. You can use it in a session scoped bean, but you have to do some finetuning and those will be explained in the text. It's recommended to use at least JSF 1.1_02 or JSF 1.2_02 (and thus not 1.x_01 or older), because the 1.x_02 has some important bugfixes. The code examples given below are based on JSF 1.2 with at least Java EE 5.0. At the bottom of this article you can download an EAR containing all examples in Request as well as Session scope using JSF 1.2. Create DTO class

The h:dataTable dynamic tables are nice, you can put any List, DataModel or Map objects in it. Imagine a SQL database containing a table with three fields: ID, Name and Value.

First create a wrapper class which represents each row of the table, this is just a simple class with the table fields (columns) definied as encapsulated private variables which can be accessed by public getters and setters. Instances of those kind of wrapper classes are also known as DTO (Data Transfer Objects). Some people might call it \"Javabeans\" because of having only getters and setters, or even POJO's (Plain Old Java Objects). Terms which are too global. Strictly looking to it's functionality this is really a DTO. Here is an example called MyData.java:

package mypackage;

public class MyData {

// Init

--------------------------------------------------------------------------------------

private Long id; private String name; private String value;

// Getters

-----------------------------------------------------------------------------------

public Long getId() { return id; }

public String getName() { return name; }

public String getValue() { return value; }

// Setters

-----------------------------------------------------------------------------------

public void setId(Long id) { this.id = id; }

public void setName(String name) { this.name = name; }

public void setValue(String value) { this.value = value; } }

It's a good practice to use wrapper datatype objects (Long, Boolean, Integer, etc) instead of primitive datatypes (long, boolean, int, etc) for the properties, because the database fields can contain null values which can not be put into a primitive whatsoever.

Back to top

Retrieve and store data

You can use Hibernate or just plain JDBC to retrieve SQL data. When retrieved, then just put the data of each row in a new MyData object and add each MyData object to one List object. Hibernate can return all rows as MyData objects in one ArrayList. This have to be stored in a private variable dataList which can be accessed by a getter and setter. The setter of the dataList is actually not used by JSF pages (only UIInput values require setters). You can safely remove it if you don't use it somewhere else in the backing bean logic.

The relevant java code for the backing bean:

package mypackage;

import java.util.List;

import javax.faces.context.FacesContext;

import mydao.*;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private List dataList;

// Actions

-----------------------------------------------------------------------------------

private void loadDataList() {

// Do your \"SELECT * FROM mydata\" thing. LoadQuery loadQuery = new LoadQuery(MyData.class); dao.execute(loadQuery);

dataList = loadQuery.getResults(); }

// Getters

-----------------------------------------------------------------------------------

public List getDataList() { if (dataList == null) {

loadDataList(); // Preload by lazy loading. }

return dataList; } }

Session scope: once loaded the dataList will persist during the session and won't be garbaged after one request in the same session. So you can load it only once using the constructor or an initialization block: public class MyBean {

// Init

--------------------------------------------------------------------------------------

{

loadDataList(); // Preload. }

// Constructor

-------------------------------------------------------------------------------

public MyBean() {

loadDataList(); // Preload. } }

Or if you want to reload the session data every view to get the most recent data, then do as follows:

public List getDataList() {

if (FacesContext.getCurrentInstance().getRenderResponse()) { loadDataList(); // Reload to get most recent data. }

return dataList; }

The FacesContext#getRenderResponse() returns true when this getter is invoked during the render response phase. This is to prevent duplicate

reloads as this getter can also be invoked during the apply request values phase or the process validations phase. Also see Debug JSF lifecycle. You can also consider to use two beans: one request-scoped bean for the views and form actions and one session-scoped bean for the session data. You can inject the session scoped bean in the request scoped bean as managed property using faces-config.xml. And in the request scoped bean you can invoke reload of the data after save/update/delete action of the session data. Also see Communication in JSF.

Back to top

Show data contents in datatable

First define the backing bean as a request scoped managed bean myBean in the faces-config.xml.

myBean

mypackage.MyBean request

Session scope: if you want to use the session scope, just change request to session.

Now you can use the h:dataTable to show the contents of the retrieved list. The relevant JSF code looks like:

The h:dataTable value should contain the retrieved list from the backing bean definied as managed bean myBean. The h:dataTable var is just a holder for every MyData object of the list. This can be used to access the getters and setters of MyData.java.

Back to top

Add backing bean action to every row

You can use h:commandLink or h:commandButton in one or more columns to invoke a backing bean action and pass the appropriate MyData object to MyBean. This is faster than passing a f:attribute tag with an ID to retrieve the selected item by ID straight from the database. To get the selected row, use the h:dataTable binding to dynamically bind the state of the datatable to the backing bean. Update the JSP as follows:

...

Back to top

Get selected datatable row

Clicking at the h:commandLink of every row will invoke the editDataItem() method of the backing bean. The MyData item belonging to the row can be retrieved using the getRowData() method of the HtmlDataTable class, which is bound by h:dataTable binding. Finally store the MyData item as dataItem.

So extend the backing bean with the following code to retrieve the MyData item of the row selected:

package mypackage;

import javax.faces.component.html.HtmlDataTable;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private HtmlDataTable dataTable;

private MyData dataItem = new MyData();

// Actions

-----------------------------------------------------------------------------------

public String editDataItem() {

// Get selected MyData item to be edited. dataItem = (MyData) dataTable.getRowData();

return \"edit\"; // Navigation case. }

// Getters

-----------------------------------------------------------------------------------

public HtmlDataTable getDataTable() { return dataTable; }

public MyData getDataItem() { return dataItem;

}

// Setters

-----------------------------------------------------------------------------------

public void setDataTable(HtmlDataTable dataTable) { this.dataTable = dataTable; }

public void setDataItem(MyData dataItem) { this.dataItem = dataItem; } }

Session scope: you don't need to pre-instantiate the dataItem with new MyData() as this won't be garbaged after one request.

Back to top

Show selected data item

The contents of the MyData item retrieved can be shown in a new JSP file for edit or just for view. In this example we will show the item for edit. The relevant JSF code:

Session scope: you don't need the h:inputHidden element as dataItem won't be garbaged after one request.

Back to top

Save edited data item

Saving is simple, the JSF lifecycle already has updated the MyData item for you. Just do your DAO update thing. The relevant java code of the backing bean:

package mypackage;

import mydao.*;

public class MyBean {

// Actions

-----------------------------------------------------------------------------------

public String saveDataItem() {

// Do your \"UPDATE mydata SET values WHERE id\" thing.

SaveQuery saveQuery = new SaveQuery(dataItem); dao.execute(saveQuery);

return \"list\"; // Navigation case. } }

Back to top

Editable datatable

You can also use the datatable to mass-edit the datalist. Also here saving the edited datalist is simple, the JSF lifecycle already has updated the dataList for you. You just need to do your DAO update thing. The relevant JSF code looks like:


The relevant java code of the backing bean:

package mypackage;

import mydao.*;

public class MyBean {

// Actions

-----------------------------------------------------------------------------------

public String saveDataList() {

// Do your \"UPDATE mydata SET values WHERE id\" thing for each data item.

SaveQuery saveQuery = new SaveQuery(dataList); dao.execute(saveQuery);

return \"list\"; // Navigation case. } }

Back to top

Select multiple rows

You also can select multiple rows using checkboxes. Just add a boolean property to the MyData.java DTO class and use h:selectBooleanCheckbox to add a checkbox column which triggers this property. Add this boolean property to the MyData.java:

package mypackage;

public class MyData {

// Init

--------------------------------------------------------------------------------------

private boolean selected;

// Getters

-----------------------------------------------------------------------------------

public boolean isSelected() { return selected; }

// Setters

-----------------------------------------------------------------------------------

public void setSelected(boolean selected) { this.selected = selected; } }

Extend the datatable with a checkbox column and add a commandbutton to the form:

...

Finally add the commandbutton action to the backing bean to get the selected items:

package mypackage;

import java.util.ArrayList; import java.util.Iterator; import java.util.List;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private List selectedDataList;

// Actions

-----------------------------------------------------------------------------------

public String getSelectedItems() {

// Get selected items.

selectedDataList = new ArrayList(); for (MyData dataItem : dataList) { if (dataItem.isSelected()) {

selectedDataList.add(dataItem);

dataItem.setSelected(false); // Reset. } }

// Do your thing with the MyData items in List selectedDataList.

return \"selected\"; // Navigation case. }

// Getters

-----------------------------------------------------------------------------------

public List getSelectedDataList() { return selectedDataList; } }

If you don't want to extend the MyData object with the boolean property selected for some reasons, then you can also consider the following approach with a Map:

value=\"#{myBean.selectedIds[dataItem.id]}\" />

...

The appropriate backing bean code: package mypackage;

import java.util.ArrayList;

import java.util.HashMap; import java.util.Iterator; import java.util.List; import java.util.Map;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private Map selectedIds = new HashMap();

private List selectedDataList;

// Actions

-----------------------------------------------------------------------------------

public String getSelectedItems() {

// Get selected items.

selectedDataList = new ArrayList(); for (MyData dataItem : dataList) {

if (selectedIds.get(dataItem.getId()).booleanValue()) { selectedDataList.add(dataItem);

selectedIds.remove(dataItem.getId()); // Reset. } }

// Do your thing with the MyData items in List selectedDataList.

return \"selected\"; // Navigation case. }

// Getters

-----------------------------------------------------------------------------------

public Map getSelectedIds() { return selectedIds; }

public List getSelectedDataList() {

return selectedDataList; } }

Back to top

Select row by radio button

Using h:selectOneRadio in a JSF datatable is a bit tricky. When putting just a h:selectOneRadio component in a h:column column, the radio buttons gets rendered, but they are not grouped together. Each radio button is attached to one row object and not to the datatable itself, resulting the other radiobuttons not getting unselected when you select one radiobutton. To get it work, use JavaScript to unselect all other radio buttons when one radio button is selected. This is only good for the human eye. The real work is done by the valuechangelistener attribute of the

h:selectOneRadio which fires a ValueChangeEvent. When you clicks a radio button and then clicks the command button, the row index of the selected row will be lost. The fired ValueChangeEvent is processed during the third phase of the JSF lifecycle, where the row index is still available. The row index is reset during the fourth phase and the command button action is invoked during the fifth phase. So the trick is to store the object of the selected row during the processing of the ValueChangeEvent and finally handle with it during the command button action. Add a h:selectOneRadio column to the datatable and supply a fake selectItem to it:

valueChangeListener=\"#{myBean.setSelectedItem}\"

onclick=\"dataTableSelectOneRadio(this);\">

...

Add the valuechangelistener and the commandbutton action to the backing bean to get the selected item and to handle with it:

package mypackage;

import javax.faces.event.ValueChangeEvent;

public class MyBean {

// Actions

-----------------------------------------------------------------------------------

public void setSelectedItem(ValueChangeEvent event) {

// Catch the MyData item during the third phase of the JSF lifecycle.

dataItem = (MyData) dataTable.getRowData(); }

public String getSelectedItem() {

// The MyData item is already catched in setSelectedItem(). selectedDataList = new ArrayList(); selectedDataList.add(dataItem);

// Do your thing with the MyData item in List selectedDataList.

return \"selected\"; // Navigation case. } }

Here is the JavaScript function which resets all other radio buttons of the same column.

function dataTableSelectOneRadio(radio) {

var id = radio.name.substring(radio.name.lastIndexOf(':')); var el = radio.form.elements;

for (var i = 0; i < el.length; i++) {

if (el[i].name.substring(el[i].name.lastIndexOf(':')) == id) { el[i].checked = false; } }

radio.checked = true; }

Back to top

Sorting datatable

As datatables can be filled with List objects, you can easily sort a datatable using Collections.sort(). The best approach is to add a h:commandLink tag with one f:attribute tag to each column header. The h:commandLink tag should invoke the sorting in the backing bean. The f:attribute tag should pass the column name/type, or in this case, the name of the field getter of the DTO class. And it should be nice to reverse the sorting when clicking again at the same column header.

Add commandlinks with params to the column headers of the datatable:

actionListener=\"#{myBean.sortDataList}\">

actionListener=\"#{myBean.sortDataList}\">

actionListener=\"#{myBean.sortDataList}\">

Session scope: you don't need the h:inputHidden elements as the current sortField and sortAscending won't be garbaged after one request. Now extend the backing bean with sortDataList() to set the sort field and the sort order. Use the f:attribute value to indicate which MyData field should be sorted. The DtoComparator is described later.

package mypackage;

import java.util.Collections;

import javax.faces.context.FacesContext; import javax.faces.event.ActionEvent;

import myutils.DtoComparator;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private String sortField = null;

private boolean sortAscending = true;

// Actions

-----------------------------------------------------------------------------------

public void sortDataList(ActionEvent event) {

String sortFieldAttribute = getAttribute(event, \"sortField\");

// Get and set sort field and sort order.

if (sortField != null && sortField.equals(sortFieldAttribute)) {

sortAscending = !sortAscending; } else {

sortField = sortFieldAttribute; sortAscending = true; }

// Sort results.

if (sortField != null) {

Collections.sort(dataList, new DtoComparator(sortField, sortAscending)); } }

// Getters

-----------------------------------------------------------------------------------

public String getSortField() { return sortField; }

public boolean getSortAscending() { return sortAscending; }

// Setters

-----------------------------------------------------------------------------------

public void setSortField(String sortField) { this.sortField = sortField; }

public void setSortAscending(boolean sortAscending) {

this.sortAscending = sortAscending; }

// Helpers

-----------------------------------------------------------------------------------

private static String getAttribute(ActionEvent event, String name) {

return (String)

event.getComponent().getAttributes().get(name); } }

Session scope: you don't need to pre-instantiate the sortField and sortAscending with null and true as those won't be garbaged after one request. You also have to move the piece of Java code after the \"// Sort results.\" comment at the end of sortDataList to the end of loadDataList. This is because the reloading of the dataList in the getDataList() method will override the sorting otherwise.

public void loadDataList() {

...

// Sort results.

if (sortField != null) {

Collections.sort(dataList, new DtoComparator(sortField, sortAscending)); } }

Here is the DtoComparator.java which can be very useful to sort data transfer objects in a List. It implements the Comparator interface. package net.balusc.util;

import java.util.ArrayList; import java.util.Arrays; import java.util.Comparator; import java.util.Iterator; import java.util.List; /**

* Sorts a collection of data transfer objects (DTO's) based on the name of the getter of the field

* to sort on. For example: the DTO MyDto has three fields ID, Name and Value, all with appropriate

* getters and setters. If you want to sort on the field Name, which has a getter called \"getName\

* then invoke the sorting as follows: *

* Collections.sort(myDtoList, new DtoComparator(\"getName\ *

* The following call is also supported. The \"get\" will be prefixed automatically if not present: *

* Collections.sort(myDtoList, new DtoComparator(\"name\ *

* You can use \"deep\" getters when a DTO contains one or more nested DTO's. For example: MyDto1

* contains a MyDto2 property which contains a String property \"name\". If you want to sort

* myDto1List on the \"name\" property of MyDto2, then separate the getters by dots and invoke the * sorting as follows: *

* Collections.sort(myDto1List, new DtoComparator(\"myDto2.namerue)); *

* The boolean 2nd parameter indicates the sort order. If true, then the collection will be sorted

* ascending at natural sort order. If false, then the collection will be sorted descending at

* natural sort order. The default value is true. *

* Very useful for lists of DTO's used in JSF datatables. *

* @author BalusC

* @link http://balusc.blogspot.com/2006/06/using-datatables.html */

public class DtoComparator implements Comparator {

// Init

--------------------------------------------------------------------------------------

private List getters;

private boolean ascending;

// Constructor

-------------------------------------------------------------------------------

/**

* @param getter The name of the getter of the field to sort on. * @param ascending The sort order: true = ascending, false = descending. */

public DtoComparator(String getter, boolean ascending) { this.getters = new ArrayList(); for (String name : getter.split(\"\\\\.\")) { if (!name.startsWith(\"get\")) {

name = \"get\" + name.substring(0, 1).toUpperCase() + name.substring(1); }

this.getters.add(name); }

this.ascending = ascending; }

/**

* @param getter The name of the getter of the field to sort on. */

public DtoComparator(String getter) { this(getter, true); }

// Actions

-----------------------------------------------------------------------------------

/**

* @see java.util.Comparator#compare(java.lang.Object, java.lang.Object) */

public int compare(Object o1, Object o2) { try {

Iterator iter = getters.iterator();

while (o1 != null && o2 != null && iter.hasNext()) { String getter = iter.next();

o1 = o1.getClass().getMethod(getter, new Class[0]).invoke(o1, new Object[0]);

o2 = o2.getClass().getMethod(getter, new Class[0]).invoke(o2, new Object[0]); }

} catch (Exception e) {

// If this exception occurs, then it is usually a fault of the DTO developer.

throw new RuntimeException(\"Cannot compare \" + o1 + \" with \" + o2 + \" on \" + getters, e); }

if (o1 == null) {

return ascending ? -1 : 1; // If ascending, current null first.

} else if (o2 == null) {

return ascending ? 1 : -1; // If ascending, another null first. }

if (ascending) {

return ((Comparable) o1).compareTo(o2); // Ascending. } else {

return ((Comparable) o2).compareTo(o1); // Descending. } } }

Back to top

Paging datatable

If you have a plenty of rows, then you can use the following datatable attributes to page through the datatable: first and rows. The first attribute identifies the row number of the starting row which should be displayed. The rows attribute identifies the amount of rows to be shown at once, from the first row on. For pagination we need to set the first attribute dynamically in the backing bean. You can use commandlinks or commandbuttons to change this value.

So extend the datatable with the rows attribute and a set of commandbuttons in the footer. In this example we will page on every 3 rows.

value=\"#{myBean.dataList}\" var=\"dataItem\" rows=\"3\"> ...

...

disabled=\"#{myBean.dataTable.first == 0}\" /> disabled=\"#{myBean.dataTable.first == 0}\" /> disabled=\"#{myBean.dataTable.first + myBean.dataTable.rows

>= myBean.dataTable.rowCount}\" /> disabled=\"#{myBean.dataTable.first + myBean.dataTable.rows

>= myBean.dataTable.rowCount}\" />

Please note the disabled attribute of the commandbuttons. The 'first' and the 'prev' buttons should be disabled at the first page. The 'next' and the 'last' buttons should be disabled when a next page cannot exist. And add the following pagination methods to the backing bean: package mypackage;

public class MyBean {

// Actions

-----------------------------------------------------------------------------------

public void pageFirst() { dataTable.setFirst(0); }

public void pagePrevious() {

dataTable.setFirst(dataTable.getFirst() - dataTable.getRows()); }

public void pageNext() {

dataTable.setFirst(dataTable.getFirst() + dataTable.getRows()); }

public void pageLast() {

int count = dataTable.getRowCount(); int rows = dataTable.getRows();

dataTable.setFirst(count - ((count % rows != 0) ? count % rows : rows)); } }

Back to top

Nesting datatables

You can use multidimensional lists in a nested datatable: just put the inner datatable in a h:column of the outer datatable and add the inner list to the DTO of the outer list. The basic JSF code:

The updated Java code of MyData.java which represents each outerItem: package mypackage;

public class MyData {

// Init

--------------------------------------------------------------------------------------

private List innerList;

// Getters

-----------------------------------------------------------------------------------

public List getInnerList() { return innerList; }

// Setters

-----------------------------------------------------------------------------------

public void setInnerList(List innerList) { this.innerList = innerList; } }

In the backing bean you can let the DAO preload the innerList into each item of the outerList. The innerList should represent a list of DTO's, like the outerList does.

The relevant java code for the backing bean:

package mypackage;

import java.util.List;

import mydao.*;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private List outerList;

// Actions

-----------------------------------------------------------------------------------

private void loadOuterList() {

// Do your \"SELECT * FROM outerdata INNER JOIN innerdata\" thing. LoadQuery loadQuery = new LoadQuery(MyData.class, true); dao.execute(loadQuery);

outerList = loadQuery.getResults(); }

// Getters

-----------------------------------------------------------------------------------

public List getOuterList() { if (outerList == null) {

loadOuterList(); // Preload by lazy loading. }

return outerList; } }

Back to top

Populate datatable

You can also programmatically populate the datatable in the backing bean. This may be useful if you don't know the expected state of the datatable in the JSF page, but only in the backing bean. Here is the basic JSF code:

Fairly simple, isn't it? We're using a h:panelGroup as placeholder, because when you want to bind the datatable itself, then you have to specify the var attribute in the JSF page. Using a h:panelGroup without any style doesn't output anything to the HTML, so this won't harm that much.

And here is the relevant Java code of the backing bean, it might look like a lot of code, but it is actually rather simple:

package mypackage;

import java.util.List;

import javax.el.ValueExpression;

import javax.faces.component.html.HtmlColumn; import javax.faces.component.html.HtmlDataTable; import javax.faces.component.html.HtmlOutputText; import javax.faces.component.html.HtmlPanelGroup;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private HtmlPanelGroup dataTableGroup; // Placeholder.

// Actions

-----------------------------------------------------------------------------------

private void populateDataTable() {

// Renew group.

dataTableGroup = new HtmlPanelGroup();

// Create datatable.

HtmlDataTable dataTable = new HtmlDataTable(); dataTable.setValueExpression(\"value\

createValueExpression(\"#{myBean.dataList}\ dataTable.setVar(\"dataItem\");

// Create 'ID' column and add to datatable. HtmlColumn idColumn = new HtmlColumn();

HtmlOutputText idHeader = new HtmlOutputText(); idHeader.setValue(\"ID\");

idColumn.setHeader(idHeader);

HtmlOutputText idOutput = new HtmlOutputText(); idOutput.setValueExpression(\"value\

createValueExpression(\"#{dataItem.id}\ idColumn.getChildren().add(idOutput); dataTable.getChildren().add(idColumn);

// Create 'Name' column and add to datatable. HtmlColumn nameColumn = new HtmlColumn();

HtmlOutputText nameHeader = new HtmlOutputText(); nameHeader.setValue(\"Name\");

nameColumn.setHeader(nameHeader);

HtmlOutputText nameOutput = new HtmlOutputText(); nameOutput.setValueExpression(\"value\

createValueExpression(\"#{dataItem.name}\ nameColumn.getChildren().add(nameOutput); dataTable.getChildren().add(nameColumn);

// Create 'Value' column and add to datatable. HtmlColumn valueColumn = new HtmlColumn();

HtmlOutputText valueHeader = new HtmlOutputText(); valueHeader.setValue(\"Value\");

valueColumn.setHeader(valueHeader);

HtmlOutputText valueOutput = new HtmlOutputText(); valueOutput.setValueExpression(\"value\

createValueExpression(\"#{dataItem.value}\String.class));

valueColumn.getChildren().add(valueOutput); dataTable.getChildren().add(valueColumn);

// Add datatable to group.

dataTableGroup.getChildren().add(dataTable); }

// Getters

-----------------------------------------------------------------------------------

public HtmlPanelGroup getDataTableGroup() {

// This will be called once in the first RESTORE VIEW phase. if (dataTableGroup == null) {

populateDataTable(); // Populate datatable. }

return dataTableGroup; }

// Setters

-----------------------------------------------------------------------------------

public void setDataTableGroup(HtmlPanelGroup dataTableGroup) { this.dataTableGroup = dataTableGroup; }

// Helpers

-----------------------------------------------------------------------------------

private ValueExpression createValueExpression(String valueExpression, Class valueType) {

FacesContext facesContext = FacesContext.getCurrentInstance(); return

facesContext.getApplication().getExpressionFactory().createValueExpression(

facesContext.getELContext(), valueExpression, valueType); } }

This creates exactly the same datatable as shown in Show data contents in datatable.

Back to top

Populate editable datatable

You can also programmatically populate an editable datatable in the backing bean. This may be useful if you don't know the expected state of the datatable in the JSF page, but only in the backing bean. Here is the basic JSF code:

Fairly simple, isn't it? We're using a h:panelGroup as placeholder, because when you want to bind the datatable itself, then you have to specify the var attribute in the JSF page. Using a h:panelGroup without any style doesn't output anything to the HTML, so this won't harm that much.

And here is the relevant Java code of the backing bean, it might look like a lot of code, but it is actually rather simple:

package mypackage;

import java.util.List;

import javax.el.MethodExpression; import javax.el.ValueExpression;

import javax.faces.component.html.HtmlColumn;

import javax.faces.component.html.HtmlCommandButton; import javax.faces.component.html.HtmlDataTable; import javax.faces.component.html.HtmlInputText; import javax.faces.component.html.HtmlOutputText; import javax.faces.component.html.HtmlPanelGroup;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private HtmlPanelGroup editableDataTableGroup; // Placeholder.

// Actions

-----------------------------------------------------------------------------------

private void populateEditableDataTable() {

// Renew group.

editableDataTableGroup = new HtmlPanelGroup();

// Create datatable.

HtmlDataTable editableDataTable = new HtmlDataTable(); editableDataTable.setValueExpression(\"value\

createValueExpression(\"#{myBean.dataList}\ editableDataTable.setVar(\"dataItem\");

// Create 'ID' column and add to datatable. HtmlColumn idColumn = new HtmlColumn();

HtmlOutputText idHeader = new HtmlOutputText(); idHeader.setValue(\"ID\");

idColumn.setHeader(idHeader);

HtmlOutputText idOutput = new HtmlOutputText(); idOutput.setValueExpression(\"value\

createValueExpression(\"#{dataItem.id}\ idColumn.getChildren().add(idOutput);

editableDataTable.getChildren().add(idColumn);

// Create editable 'Name' column and add to datatable. HtmlColumn nameColumn = new HtmlColumn();

HtmlOutputText nameHeader = new HtmlOutputText(); nameHeader.setValue(\"Name\");

nameColumn.setHeader(nameHeader);

HtmlInputText nameInput = new HtmlInputText();

nameInput.setId(\"name\"); // Custom ID is required in dynamic UIInput and UICommand.

nameInput.setValueExpression(\"value\

createValueExpression(\"#{dataItem.name}\ nameColumn.getChildren().add(nameInput);

editableDataTable.getChildren().add(nameColumn);

// Create editable 'Value' column and add to datatable. HtmlColumn valueColumn = new HtmlColumn();

HtmlOutputText valueHeader = new HtmlOutputText(); valueHeader.setValue(\"Value\");

valueColumn.setHeader(valueHeader);

HtmlInputText valueInput = new HtmlInputText();

valueInput.setId(\"value\"); // Custom ID is required in dynamic UIInput and UICommand.

valueInput.setValueExpression(\"value\

createValueExpression(\"#{dataItem.value}\String.class));

valueColumn.getChildren().add(valueInput);

editableDataTable.getChildren().add(valueColumn);

// Add datatable to group.

editableDataTableGroup.getChildren().add(editableDataTable);

// Create HTML line break and add to group.

HtmlOutputText lineBreak = new HtmlOutputText(); lineBreak.setValue(\"
\");

lineBreak.setEscape(false); // Don't escape HTML.

editableDataTableGroup.getChildren().add(editableDataTable);

// Create 'Save' commandButton and add to group.

HtmlCommandButton saveButton = new HtmlCommandButton();

saveButton.setId(\"save\"); // Custom ID is required in dynamic UIInput and UICommand.

saveButton.setValue(\"Save\"); saveButton.setActionExpression(

createActionExpression(\"#{myBean.saveDataList}\String.class));

editableDataTableGroup.getChildren().add(saveButton); }

// Getters

-----------------------------------------------------------------------------------

public HtmlPanelGroup getEditableDataTableGroup() {

// This will be called once in the first RESTORE VIEW phase. if (editableDataTableGroup == null) {

populateEditableDataTable(); // Populate editable datatable. }

return editableDataTableGroup; }

// Setters

-----------------------------------------------------------------------------------

public void setEditableDataTableGroup(HtmlPanelGroup editableDataTableGroup) {

this.editableDataTableGroup = editableDataTableGroup; }

// Helpers

-----------------------------------------------------------------------------------

private ValueExpression createValueExpression(String valueExpression, Class valueType) {

FacesContext facesContext = FacesContext.getCurrentInstance(); return

facesContext.getApplication().getExpressionFactory().createValueExpression(

facesContext.getELContext(), valueExpression, valueType); }

private MethodExpression createActionExpression(String actionExpression, Class returnType) {

FacesContext facesContext = FacesContext.getCurrentInstance(); return

facesContext.getApplication().getExpressionFactory().createMethodExpression(

facesContext.getELContext(), actionExpression, returnType, new Class[0]); } }

This creates exactly the same datatable as shown in Editable datatable.

Back to top

Populate dynamic datatable

You also can dynamically populate a datatable in the backing bean. This may be very useful if you are using dynamic generated two-dimensional lists (for example, uploaded CSV files, local text files, DB fields, etc) and you don't know the amount of columns before. The basic JSF code:

The relevant Java code of the backing bean:

package mypackage;

import java.util.ArrayList; import java.util.Arrays; import java.util.List;

import javax.el.ValueExpression;

import javax.faces.component.HtmlColumn; import javax.faces.component.HtmlOutputText;

import javax.faces.component.html.HtmlDataTable; import javax.faces.context.FacesContext;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private static List> dynamicList; // Simulate fake DB. private static String[] dynamicHeaders; // Optional.

private HtmlPanelGroup dynamicDataTableGroup; // Placeholder.

// Actions

-----------------------------------------------------------------------------------

private void loadDynamicList() {

// Renew group.

dynamicDataTableGroup = new HtmlPanelGroup();

// Set headers (optional).

dynamicHeaders = new String[] {\"ID\

// Set rows. This is a stub example, just do your dynamic thing. dynamicList = new ArrayList>();

dynamicList.add(Arrays.asList(new String[] { \"ID1\\"Value1\" };

dynamicList.add(Arrays.asList(new String[] { \"ID2\\"Value2\" };

dynamicList.add(Arrays.asList(new String[] { \"ID3\\"Value3\" };

dynamicList.add(Arrays.asList(new String[] { \"ID4\\"Value4\" };

dynamicList.add(Arrays.asList(new String[] { \"ID5\\"Value5\" }; }

private void populateDynamicDataTable() {

// Create datatable.

HtmlDataTable dynamicDataTable = new HtmlDataTable(); dynamicDataTable.setValueExpression(\"value\

createValueExpression(\"#{myBean.dynamicList}\List.class));

dynamicDataTable.setVar(\"dynamicItem\");

// Get amount of columns.

int columns = dynamicList.get(0).size();

// Loop through columns.

for (int i = 0; i < columns; i++) {

// Create column.

HtmlColumn column = new HtmlColumn();

// Create header (optional) and add to column. HtmlOutputText header = new HtmlOutputText(); header.setValue(dynamicHeaders[i]); column.setHeader(header);

// Create output and add to column.

HtmlOutputText output = new HtmlOutputText(); output.setValueExpression(\"value\

createValueExpression(\"#{dynamicItem[\" + i + \"]}\String.class));

column.getChildren().add(output);

// Add column to datatable.

dynamicDataTable.getChildren().add(column); }

dynamicDataTableGroup.getChildren().add(dynamicDataTable); }

// Getters

-----------------------------------------------------------------------------------

public HtmlPanelGroup getDynamicDataTableGroup() {

// This will be called once in the first RESTORE VIEW phase. if (dynamicDataTableGroup == null) {

loadDynamicList(); // Preload dynamic list. populateDynamicDataTable(); // Populate editable datatable. }

return dynamicDataTableGroup; }

public List> getDynamicList() { return dynamicList; }

// Setters

-----------------------------------------------------------------------------------

public void setDynamicDataTableGroup(HtmlPanelGroup dynamicDataTableGroup) {

this.dynamicDataTableGroup = dynamicDataTableGroup; } }

Back to top

Add row numbers

This is fairly simple to implement using getRowIndex() method of the HtmlDataTable class, which is bound by h:dataTable binding.

...

We are using + 1 in the value binding expression, because the row index usually starts with 0.

Back to top

Alternating rows

You can alternate the rows using the dataTable attribute rowClasses and some piece of CSS. Put in there at least two style class names and the rows will apply those styles concurrently and repeating. Here is a basic example:

...

Here is the CSS defining the row1 and row2 styles. This is just a basic example, you can define your own colors for example. .row1 {

background-color: #ddd; }

.row2 {

background-color: #bbb; }

Back to top

Highlight rows on click

You can use Javascript and DOM to alter the rows of the dataTable by adding an onclick function to it which triggers a Javascript function. Here is a basic Javascript example:

function addOnclickToDatatableRows() { var trs =

document.getElementById('dataTable').getElementsByTagName('tbody')[0] .getElementsByTagName('tr');

for (var i = 0; i < trs.length; i++) {

trs[i].onclick = new Function(\"highlightRow(this)\"); } }

function highlightRow(tr) {

tr.bgColor = (tr.bgColor != '#ff0000') ? '#ff0000' : '#ffffff'; }

Call addOnclickToDatatableRows() at the very end of your body tag. Take note that the element ID in the Javascript have to be exactly the same as the generated element ID of the table.

...

...

Back to top

Highlight and select row on click

You can also highlight single row and select the row for submit using an onclick function on the table row. Basically just pass its row index to a plain vanilla HTML hidden input. Here is a basic Javascript example: function addOnclickToDatatableRows() { var trs =

document.getElementById('form:dataTable').getElementsByTagName('tbody')[0]

.getElementsByTagName('tr');

for (var i = 0; i < trs.length; i++) {

trs[i].onclick = new Function(\"highlightAndSelectRow(this)\"); } }

function highlightAndSelectRow(tr) { var trs =

document.getElementById('form:dataTable').getElementsByTagName('tbody')[0]

.getElementsByTagName('tr');

for (var i = 0; i < trs.length; i++) {

if (trs[i] == tr) {

trs[i].bgColor = '#ff0000';

document.form.rowIndex.value = trs[i].rowIndex; } else {

trs[i].bgColor = '#ffffff'; } } }

Call addOnclickToDatatableRows() at the very end of your body tag. Take note that the element ID in the Javascript have to be exactly the same as the generated element ID of the form and the table.

...

...

Here is the relevant part of the backing bean code. This achieves exactly the same actions as shown in Add backing bean action to every row and Get selected datatable row:

package mypackage;

import java.util.List;

import javax.faces.context.FacesContext;

public class MyBean {

// Init

--------------------------------------------------------------------------------------

private List dataList;

private MyData dataItem = new MyData();

// Actions

-----------------------------------------------------------------------------------

public String editDataItem() {

// Obtain the row index from the hidden input element. String rowIndex =

FacesContext.getCurrentInstance().getExternalContext() .getRequestParameterMap().get(\"rowIndex\");

if (rowIndex != null && rowIndex.trim().length() != 0) { dataItem = dataList.get(Integer.parseInt(rowIndex)); } else {

// Handle unexpected state, e.g. show message \"Please select row\" or so. }

return \"edit\"; // Navigation case. } }

Back to top

Highlight rows on hover

You can use Javascript and DOM to alter the rows of the dataTable by adding an onmouseover and onmouseout function to it which triggers a Javascript function. Here is a basic Javascript example:

function addHoverToDatatableRows() { var trs =

document.getElementById('dataTable').getElementsByTagName('tbody')[0] .getElementsByTagName('tr');

for (var i = 0; i < trs.length; i++) {

trs[i].onmouseover = new Function(\"this.bgColor='#ff0000'\"); trs[i].onmouseout = new Function(\"this.bgColor='#ffffff'\"); }

}

Call addHoverToDatatableRows() at the very end of your body tag. Take note that the element ID in the Javascript have to be exactly the same as the generated element ID of the table.

...

...

Back to top

Customized tables

If you're using at least JSF 1.2 and JSTL 1.2 in a JSP 2.1 container (available in Java EE 5 server, e.g. Tomcat 6.0 or Glassfish), then you can access JSF managed beans in the JSTL c:forEach tag, thanks to the unified EL. This will give you more freedom to create exotic tables based on a collection of DTO's from a managed bean, e.g. introducing colspans and so on.

<%@ taglib uri=\"http://java.sun.com/jsp/jstl/core\" prefix=\"c\"%> <%@ taglib uri=\"http://java.sun.com/jsf/core\" prefix=\"f\"%> <%@ taglib uri=\"http://java.sun.com/jsf/html\" prefix=\"h\"%> ...

MASTER TABLE
ID NameValue
This is just an example of an alternately inserted row.

This creates a table with exactly the same behaviour as the table as shown in Add backing bean action to every row with the difference that you can introduce colspans to your taste. The example also shows how to use f:verbatim whether to render plain vanilla HTML or not. The example also shows how to alternate the row colors using the c:forEach varStatus. The f:setPropertyActionListener (which was introduced in JSF 1.2 and only works inside UICommand components) will set the current dataItem object in the appropriate property of the backing bean. It is the same dataItem object as you should obtain by HtmlDataTable#getRowData() when using a regular h:dataTable.

Back to top

Download EAR

Here is an EAR containing all of the above examples in Request as well as Session scope. This EAR also contains \"The ultimate CRUD example\" which is a show-off of the capacities of datatables (and JSF itself). The examples in this EAR does not cover the whole article. I am still working on a new EAR which would also include an updated DAO/ORM.

UsingDatatables12EAR.ear (2. MB, 2007-05-24) based on JSF 1.2 in a Java EE 5.0 environment.

Those EAR's are developed and tested in Eclipse 3.2 using the GlassFish Application Server V1 B48.

Back to top

因篇幅问题不能全部显示,请点此查看更多更全内容

Copyright © 2019- niushuan.com 版权所有 赣ICP备2024042780号-2

违法及侵权请联系:TEL:199 1889 7713 E-MAIL:2724546146@qq.com

本站由北京市万商天勤律师事务所王兴未律师提供法律服务