Easily disable sorting in PrimeFaces 3's DataTable
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" > <p:column headerText="#{headerText}" sortBy="#{value}"> <h:outputText id="#{id}" value="#{value}" /> </p:column> </ui:composition>
Such a column can be used on a page inside a DataTable
as follows:
<my:sortableColumn id="foo" value="#{someBean.someValue}" />
The problem
The above is only a simple example, and real-life usage can be more complex with e.g. default styles and cell editing capabilities added. With such complex tags, it might be desirable to turn off sorting for certain columns programmatically. By default this does not seem to be possible in PrimeFaces. Setting the value
attribute in the above example to null or the empty string via an expression prevents sorting, but still renders the sorting icon.
The reason for this is that the PrimeFaces renderer (org.primefaces.component.datatable.DataTableRenderer
) checks for the presence of a value expression to determine whether the icon should be rendered, regardless of whether this expression evaluates to something non-empty. So nothing you pass into the value
attribute of the above showed tag can make the value expression itself null, nor can any extra attribute accomplish this in the tag's definition.
Custom helper tag
One solution is to build a simple helper tag that takes a parameter and based on that sets the value expression of its parent component to null. This tag can be nested inside a column. Taking it one step further, we can nest the tag inside a table and programmatically disable sorting on all columns. The following shows the implementation of such a tag:
public class ColumnSorterDisabler extends TagHandler { public ColumnSorterDisabler(TagConfig config) { super(config); } @Override public void apply(FaceletContext ctx, UIComponent parent) throws IOException { Boolean disableSorting = getRequiredAttribute("disableSorting").getBoolean(ctx); if (disableSorting) { if (parent instanceof Column) { parent.setValueExpression("sortBy", null); } else if (parent instanceof DataTable) { for (UIComponent child : parent.getChildren()) { if (child instanceof Column) { child.setValueExpression("sortBy", null); } } } } } }
After declaring this in a *-taglib.xml
, we can use it as follows inside a Facelets tag:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" > <my:columnSorterDisabler disableSorting="#{disableSorting}" /> <p:column headerText="#{headerText}" sortBy="#{value}"> <h:outputText id="#{id}" value="#{value}" /> </p:column> </ui:composition>
We can now disable sorting on a per-column basis as follows:
<my:sortableColumn id="foo" value="#{someBean.someValue}" disableSorting="true" />
or for an entire table:
<p:dataTable value="#{someBean.someValues}" var="something"> <my:sortableColumn id="foo" value="#{something.foo}" /> <my:sortableColumn id="bar" value="#{something.bar}" /> <my:columnSorterDisabler disableSorting="true" /> </p:dataTable>
One caveat to remember, TagHandler
s exist only at 'build-time', so when using expressions to do the disabling the data these point to has to be available during build-time.
The f:attributes tag
Another solution, contributed by my co-worker Bauke Scholtz, is taking advantage of the <f:attribute>
tag. This tag does the same thing as an actual tag attribute, but contrary to an actual attribute this one can be conditionally excluded using <c:if>
. In that case the implementation of our re-usable column becomes this:
<ui:composition xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:p="http://primefaces.org/ui" > <p:column headerText="#{headerText}"> <c:if test="#{empty sortable or sortable}"> <f:attribute name="sortBy" value="#{value}"/> </c:if> <h:outputText id="#{id}" value="#{value}" /> </p:column> </ui:composition>
Notice how the tag attribute sortBy
has been replaced by a child tag. This approach is really convenient since it simply uses JSF's build-in functionality. To disable sorting for all columns at once, the helper tag based approach is still useful though. Note that just like the helper tag, a <f:attribute>
tag only exists at build-time, so here too only data that's available during that time can be used.
Arjan Tijms
Comments
Post a Comment