Having the end-user hide and display columns in a JSF Table Component

2

Yesterday I was discussing the migration of a Web PL/SQL based application to the Oracle ADF Technology Stack, most particularly to Java Server Faces. One interesting feature the customer would like to add to the application sometime during the migration was the ability for end-users to turn on and off the columns in a table component. The need for this feature was fairly obvious: some of the records presented in tables had over 30 or more columns and these could clearly not all be shown at the same time. However, the requirements as to which column should be visible were very different for different users at various times. So rather than pre-program the columns to be displayed in the table layout – and presenting the others in a table overflow area or a form layout detail-page, the customer preferred to leave this decision up to the end user, at run time.

My initial thought was: nice challenge, a real nut to crack. However, when I sat down to actually do it, I was a little disappointed: it was very easy! Not something to boast about at the Friday afternoon party at Cafe@AMIS – our company bar. However, since the result is quite nice, I will tell you how to do it anyway. Note: the next challenge would be ‘how to allow the end user to rearrange the columns’. A little tougher but probably not too much of a problem either.

....
 

The intended functionality is that the user can check the checkboxes in the panelbox on the right to switch on or off additional columns. For example:

It turns out to be quite simple to implement this functionality. The starting point for my How To is shown below:

This result could have been arrived at my diligent manual programming, JDeveloper IDE supported Drag & Drop, JHeadstart powered generation or any other means you can think of.

What I am trying to achieve now, is that a number of the columns currently shown in this table – Hiredate, Salary, Commission and Manager – are only shown if the user so desires. So I want to give the end user control over part of the content structure of the page.

Note: the example is described in terms of ADF Faces aka Apache MyFaces Trinidad, Oracle’s JSF implementation, but should be applicable to any JSF Table Component implementation.

Steps to implement

The key to this functionality is the fact that each Column inside the Table Component has a rendered property. This property can be fixed – which would be a little unusual if the fixed value differs from true – or it can be set dynamically through an EL expression. So what we need to do is link the rendered property of the four columns we want to give the end user control over, to  components – for example check boxes – the end user can manipulate.
 

1. Create Checkboxes for the end-user to switch on or off the columns

The first step to take is to provide a UI element to the end user that can be used to switch on or off columns. What I have done in this particular example is wrap the table component in a panelHorizontal component. To the panelHorizontal, I have added a spacer and a panelBox. Inside the panelBox is a selectManyCheckbox component, with checkboxes for each of the columns to be manipulated. Note that in this example, the names of the columns are hard-coded into the selectManyCheckbox component. To make this solution for dynamic and more generic, these values would typically be provided by a backing bean instead.

&lt;/af:table&gt;<br />  &lt;af:objectSpacer width=&quot;20&quot;/&gt;<br />  &lt;af:panelBox&gt;<br />    &lt;af:outputLabel for=&quot;cols&quot;<br />                    value=&quot;Check the Columns to display&quot;/&gt;<br />    &lt;af:selectManyCheckbox value=&quot;#{tableBean.columns}&quot;<br />                           tip=&quot;The columns to be displayed in the table&quot;<br />                           layout=&quot;vertical&quot; autoSubmit=&quot;true&quot;<br />                           id=&quot;colsMonitor&quot;&gt;<br />      &lt;af:selectItem label=&quot;Hiredate&quot; value=&quot;hd&quot;/&gt;<br />      &lt;af:selectItem label=&quot;Salary&quot; value=&quot;sal&quot;/&gt;<br />      &lt;af:selectItem label=&quot;Commission&quot; value=&quot;com&quot;/&gt;<br />      &lt;af:selectItem label=&quot;Manager&quot; value=&quot;mgr&quot;/&gt;<br />    &lt;/af:selectManyCheckbox&gt;<br />  &lt;/af:panelBox&gt;<br />&lt;/af:panelHorizontal&gt;<br /><img vspace="0" hspace="0" border="0" align="bottom" src="http://technology.amis.nl/wp-content/uploads/images/hideShowColumns_panelbox.jpg" />&nbsp;

2.  Create the backing bean tableBean to hold the values of the selected checkboxes

The value property of the selectManyCheckbox component is bound to the columns property of a managed bean called tableBean. We need to define this bean in the faces-config.xml file as well as code its implementation.

The entry in the faces-config.xml looks as follows – where I initialize the selection of columns to be displayed (and the checkboxes to be checked) to Hiredate and Salary:

&lt;managed-bean&gt;<br />  &lt;managed-bean-name&gt;tableBean&lt;/managed-bean-name&gt;<br />  &lt;managed-bean-class&gt;nl.amis.view.TableBean&lt;/managed-bean-class&gt;<br />  &lt;managed-bean-scope&gt;session&lt;/managed-bean-scope&gt;<br />  &lt;managed-property&gt;<br />    &lt;property-name&gt;columns&lt;/property-name&gt;<br />    &lt;list-entries&gt;<br />      &lt;value&gt;sal&lt;/value&gt;<br />      &lt;value&gt;hd&lt;/value&gt;<br />    &lt;/list-entries&gt;<br />  &lt;/managed-property&gt;<br />&lt;/managed-bean&gt;<br />&nbsp;

The implementation of TableBean itself is straightforward and very generic: no reference to Employees and the columns in the table component of this example are used at all.

package nl.amis.view;<br /><br />import java.util.Arrays;<br />import java.util.HashMap;<br />import java.util.Map;<br /><br />public class TableBean {<br />    public TableBean() {<br />    }<br /><br />    String[] columns;<br />    Map colMap = new HashMap();<br /><br /><br />    public void setColumns(String[] columns) {<br />        this.columns = columns;<br />        colMap.clear();<br />        for (int i = 0; i &lt; columns.length; i++) {<br />            colMap.put(columns[i], Boolean.TRUE);<br />        }<br />    }<br /><br />    public String[] getColumns() {<br />        return columns;<br />    }<br /><br />    public Map getColumnsMap() {<br />        return colMap;<br />    }<br />}<br />&nbsp;

 

3. Link the Column’s rendered property to the corresponding checkbox 

 

Finally we need to tie the checkbox representing the column and specifically its whereabouts to the column itself. The rendered property governs whether or not a column is displayed. So that is the property we need to tie to the relevant checkbox . One fairly easy way of doing this – hence the use of the getColumnsMaps() method in the TableBean – is through a Map. We can now use expressions such as the following:

 &lt;af:column sortable=&quot;true&quot; noWrap=&quot;true&quot; sortProperty=&quot;Comm&quot;<br />            formatType=&quot;number&quot;<br />            rendered=&quot;#{tableBean.columnsMap['com']==true}&quot;&gt; <br />

For each of the four columns we set the rendered property like this, using hd, sal and mgr as
the map keys, corresponding to the valu
es assigned to the checkboxes in the selectManyCheckbox component.

 

4. AJAX enable the End User’s Column Control to refresh (only) the table immediately

The last step for a true modern, rich client implementation has us AJAX enable the hide/show of columns. Note: this step is not JSF generic but ADF Faces specific. We need to do two things:

a.  The selectManyCheckbox component must always immediately submit any change in its settings to the server (in an asynchronous call). All we need to do for that to happen, is set the autoSubmit property of this component to true:

&lt;af:selectManyCheckbox value=&quot;#{tableBean.columns}&quot;<br />                       tip=&quot;The columns to be displayed in the table&quot;<br />                       layout=&quot;vertical&quot; autoSubmit=&quot;true&quot;<br />                       id=&quot;colsMonitor&quot;&gt;<br />&nbsp;

b. We need to have the table component refreshed when the asynchronous request-response cycle initiated by the selectManyCheckbox colsMonitor completes by processing the response in the client. This is equally simple: we need to include the id of the selectManyCheckbox – colsMonitor – in the partialTriggers property of the table component. That is all to get AJAX rocking our application. Boy, are we hip or what?

&lt;af:table id=&quot;EmpTableTable&quot; value=&quot;#{EmpTableCollectionModel}&quot;<br />          selectionState=&quot;#{EmpTableCollectionModel.selectedRow}&quot;<br />          selectionListener=&quot;#{EmpTableCollectionModel.makeCurrent}&quot;<br />          disclosureListener=&quot;#{EmpTableCollectionModel.hideAllDetails}&quot;<br />          var=&quot;row&quot; rows=&quot;#{bindings.EmpTableTable.rangeSize}&quot;<br />          first=&quot;#{bindings.EmpTableTable.rangeStart}&quot;<br />          emptyText=&quot;#{bindings.EmpTableTable.viewable ? 'No rows yet.' : 'Access Denied.'}&quot;<br />          binding=&quot;#{EmpTableCollectionModel.table}&quot;<br />          partialTriggers=&quot;colsMonitor&quot;&gt;<br /><br />

Share.

About Author

Lucas Jellema, active in IT (and with Oracle) since 1994. Oracle ACE Director for Fusion Middleware. Consultant, trainer and instructor on diverse areas including Oracle Database (SQL & PLSQL), Service Oriented Architecture, BPM, ADF, Java in various shapes and forms and many other things. Author of the Oracle Press book: Oracle SOA Suite 11g Handbook. Frequent presenter on conferences such as JavaOne, Oracle OpenWorld, ODTUG Kaleidoscope, Devoxx and OBUG. Presenter for Oracle University Celebrity specials.

2 Comments

  1. hello friend, I’m wanting to get selected values from my table, which is selectManyTabletype in my bean I have this method bancking

    public void imprimirFacturas(ActionEvent event){
    DCIteratorBinding iteratorFactura = (DCIteratorBinding) ADFUtils.findIterator(“FacturasView1Iterator”);
    CoreTable tabla = (CoreTable)fctx.getViewRoot().findComponent(“form1:tableFact”);
    Set selection = tabla.getSelectionState().getKeySet();
    Iterator rowSetIter = selection.iterator();
    while (rowSetIter.hasNext()) {
    Key key = (Key)((List)rowSetIter.next()).get(0);
    iteratorFactura.setCurrentRowWithKey(key.toStringFormat(true));
    RowImpl prRow = (RowImpl) iteratorFactura.getCurrentRow();
    FacturasView_rRowImpl f = (FacturasView_rRowImpl) prRow.getDataProvider();
    System.out.println(“Seleccionamos: ” + f.getAttribute(FacturasView_rRowImpl.NUMFACT));
    }
     
    }
     
    but in the first line inside the loop gives me java.lang.ClassCastException:java.lang.Integer, because I could give this exception, thanks for your help, Greetingsfrom Asuncion Paraguay

  2. Hi,

    Is this feature available for ADF/JClient also?

    We also have a similar requirement for our ADF Swing based application.

    Thanks and regards,
    Makrand