Dynamic Dashboard with PrimeFaces

I do a fair bit of work with the PrimeFaces JSF library and it is, IMHO, the best set of JSF widgets currently available. PrimeFaces has an excellent demonstration site as well which gives examples of the most common usage patterns of each widget available. Occasionally though it’s necessary to stray outside what is demonstrated and one such area is with backing beans. In the project I’m currently working on I need to make a dynamic dashboard and this is how I’ve done it. 

Update

Since first writing this article I’ve modified this design so that it can be used in an ajax environment (you’ll find the code below doesn’t work very well with ajax) the new article can be found here.

The dashboard widget uses PrimeFaces panels in order to display content in a grid. The standard way to use it involves creating a page and placing named panels on it then arranging them as you see fit. This is great if you know up front what it is you want to display but I will be providing dozens of possible dashboard widgets that the end users can pick from. In fact if all goes to plan they will be able to make up their own components for display!

The solution to the static dashboard problem is to use a backing bean for the dashboard widget (also called a binding). This is a bean that has complete control over all aspects of the component that it is bound to and so it can dynamically add and remove parts of the dashboard as necessary. The code below shows a really simple example of this in action:

package example.dashboard;

import javax.enterprise.context.RequestScoped;
import javax.faces.application.Application;
import javax.faces.component.html.HtmlOutputText;
import javax.faces.context.FacesContext;
import javax.inject.Named;
import org.primefaces.component.dashboard.Dashboard;
import org.primefaces.component.panel.Panel;
import org.primefaces.model.DashboardColumn;
import org.primefaces.model.DashboardModel;
import org.primefaces.model.DefaultDashboardColumn;
import org.primefaces.model.DefaultDashboardModel;

@Named
@RequestScoped
public class DashboardBacker {

	public static final int DEFAULT_COLUMN_COUNT = 3;
	private int columnCount = DEFAULT_COLUMN_COUNT;
	
	private Dashboard dashboard;

	public DashboardBacker() {
		FacesContext fc = FacesContext.getCurrentInstance();
		Application application = fc.getApplication();

		dashboard = (Dashboard) application.createComponent(fc, "org.primefaces.component.Dashboard", "org.primefaces.component.DashboardRenderer");
		dashboard.setId("dashboard");

		DashboardModel model = new DefaultDashboardModel();
		for( int i = 0, n = getColumnCount(); i < n; i++ ) {
			DashboardColumn column = new DefaultDashboardColumn();
			model.addColumn(column);
		}
		dashboard.setModel(model);

		int items = 5;
		
		for( int i = 0, n = items; i < n; i++ ) {
			Panel panel = (Panel) application.createComponent(fc, "org.primefaces.component.Panel", "org.primefaces.component.PanelRenderer");
			panel.setId("measure_" + i);
			panel.setHeader("Dashboard Component " + i);
			panel.setClosable(true);
			panel.setToggleable(true);

			getDashboard().getChildren().add(panel);
			DashboardColumn column = model.getColumn(i%getColumnCount());
			column.addWidget(panel.getId());
			HtmlOutputText text = new HtmlOutputText();
			text.setValue("Dashboard widget bits!" );

			panel.getChildren().add(text);
		}
	}

	public Dashboard getDashboard() {
		return dashboard;
	}

	public void setDashboard(Dashboard dashboard) {
		this.dashboard = dashboard;
	}

	public int getColumnCount() {
		return columnCount;
	}

	public void setColumnCount(int columnCount) {
		this.columnCount = columnCount;
	}
}

The example, while simplistic, shows the important aspects of using a backing bean with the dashboard component. The number of columns is dynamic as is the content and the content is spread evenly over the columns that are available. To use this backing bean in a page you simply need to add this mark up:

<p:dashboard id="dynamic_dashboard" binding="#{dashboardBacker.dashboard}"/>

Note: Extra credit to anyone who can find more than three copies of this code on the Prime Faces forums.

References

Inspiration for this article came from this post on the PrimeFaces forum.