Dynamic DataTable with JSF 2.0

A project I’m working on recently presented me with the problem of totally dynamically creating a data table on a JSF page. At first sight I assumed this would be fairly trivial since I had created a number of other components dynamically with no problems. The difference though was that the other components (for example a line chart) were backed by a clear model and they just displayed the data the model provided. A data table on the other hand is typically provided with a list of objects and columns then pick pieces of data for display.

This difference in behaviour had me stumped for a while so I thought I’d share the solution. Normally a good Googling will turn up a solution but this time I had no such luck. Every solution I could find relied on a value binding expression for the value attribute of the data table which pointed at some list of objects in a managed bean. That wasn’t a solution that would fly for me as I have no way of knowing ahead of time how many data tables I would have (zero to many) and no bean to add the data too. The obvious solution seemed to be to manually called the setValue method which is what I finally ended up doing:

package example.simpleproject;

import javax.annotation.PostConstruct;
import javax.el.ELContext;
import javax.el.ExpressionFactory;
import javax.el.ValueExpression;
import javax.enterprise.context.RequestScoped;
import javax.faces.application.Application;
import javax.faces.component.UIOutput;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.context.FacesContext;
import javax.faces.model.ArrayDataModel;
import javax.inject.Named;
import org.primefaces.component.column.Column;
import org.primefaces.component.datatable.DataTable;
import org.primefaces.component.panel.Panel;

@Named
@RequestScoped
public class DynamicDatatable {

	private Panel panel;
	
	public DynamicDatatable() {
	}
	
	@PostConstruct
	public void init() {
		FacesContext fc = FacesContext.getCurrentInstance();
		Application application = fc.getApplication();

		panel = (Panel)application.createComponent(Panel.COMPONENT_TYPE);
		panel.setHeader( "Panel" );

		refreshChildren();
	}
	
	private void refreshChildren() {
		panel.getChildren().clear();
		
		FacesContext fc = FacesContext.getCurrentInstance();
		Application application = fc.getApplication();
		ExpressionFactory ef = application.getExpressionFactory();
		ELContext elc = fc.getELContext();
		
		//Model
		String[][] rows = new String[][]{ { "1", "Foo" }, { "2", "Bar" },  {"3", "Baz" } };
		ArrayDataModel model = new ArrayDataModel<>( rows );

		//Table
		DataTable table = (DataTable) application.createComponent(DataTable.COMPONENT_TYPE);
		table.setValue(model);
		table.setVar("item");
		UIOutput tableTitle = (UIOutput)application.createComponent(UIOutput.COMPONENT_TYPE);
		tableTitle.setValue("Table Title");
		table.getFacets().put("header", tableTitle);

		//Index
		Column indexColumn = (Column) application.createComponent(Column.COMPONENT_TYPE);
		UIOutput indexColumnTitle = (UIOutput)application.createComponent(UIOutput.COMPONENT_TYPE);
		indexColumnTitle.setValue("Index");
		indexColumn.getFacets().put("header", indexColumnTitle);
//		table.getColumns().add(column);
		table.getChildren().add( indexColumn );
		
		ValueExpression indexValueExp = ef.createValueExpression(elc, "#{item[0]}", Object.class);
		HtmlOutputText indexOutput = (HtmlOutputText)application.createComponent( HtmlOutputText.COMPONENT_TYPE );
		indexOutput.setValueExpression("value", indexValueExp);
		indexColumn.getChildren().add( indexOutput );	
		
		//Name Column
		Column nameColumn = (Column) application.createComponent(Column.COMPONENT_TYPE);
		UIOutput nameColumnTitle = (UIOutput)application.createComponent(UIOutput.COMPONENT_TYPE);
		nameColumnTitle.setValue("Name");
		nameColumn.getFacets().put("header", nameColumnTitle);
		table.getChildren().add( nameColumn );

		ValueExpression nameValueExp = ef.createValueExpression(elc, "#{item[1]}", Object.class);
		HtmlOutputText nameOutput = (HtmlOutputText)application.createComponent( HtmlOutputText.COMPONENT_TYPE );
		nameOutput.setValueExpression("value", nameValueExp);
		nameColumn.getChildren().add( nameOutput );		

		panel.getChildren().add(table);
	}
	
	public Panel getDataTable() {
		refreshChildren();
		return panel;
	}
}

The example code creates a simple data table with a header and two columns. The first column contains the “index” the second the “name”. In real life you’d probably create the columns in a loop but for this example I showed them created separately. Note that you add the column as a child rather than a column (see the commented out line with the first column). I’m not entirely sure why but adding it directly results in the column not being displayed.

In order to generate the rows you have to set the var attribute on the table and then create value binding expressions for the columns. Don’t forget to provide the ELContext to the call to create the value expression or you’ll get no useful output.

In this example I’ve used the simplest data model going, just a 2D String array but it would be trivial to create a more complex data model. There are several utility data models in the javax.faces.model package which may fit the bill or it’s fairly simple to develop one.

References