JSF: Using DataTable with nested CommandLink or, CommandButton

Summery

This objective of this article is to present a common but somewhat tricky issue of JSF data table with nested commandLink or, commandButton.

 

Details

As you might have already seen that it is not possible to use commandLink inside a datatable in JSF. It is possible however, if you write business logic in your getter method. But this is not recommended as because getter methods are invoked multiple times (according to JSF architecture). If you visit any of the following 3 articles, you will get an idea what I’m talking about: 

 

– JSF: DataTable and CommandLink

URL:

http://typo.ars-subtilior.com/articles/2007/02/07/jsf-datatable-and-commandlink

 

– JavaServer Faces – CommandLink inside a DataTable

URL: http://forums.sun.com/thread.jspa?threadID=5116147

 

– Avoid this common JSF mistake

URL: http://www.mojavelinux.com/blog/archives/2008/05/avoid_this_common_jsf_mistake/

 

The authors have proposed good solutions to this problem. Some mentioned about using a custom component, some suggested about using the SEAM framework. Well, if you are still using JSF without SEAM and do not want to write a custom component, then you can go through the steps that will be mentioned here.

 

What is the Solution then?

Well, the simple solution is to use Tomahawk’s preserveDataModel attribute to “true“. There is no problem using it. Does not matter how many times your getter method (in managed bean) is invoked, you will only have to keep in mind that you DO NOT write business logic in getter method.

Here is an example:

 

UI Layer: The XHTML page:

<rich:panel>

<a4j:commandButton value=”SEARCH” id=”searchBtnId” 

action=”#{propertyMgtBeanAction.processSearchAction}”/>

</rich:panel>


<t:dataTable id=”propertiesWithStatusListDataTblId” 

value=”#{propertyMgtBeanAction.listOfPropertyServices}” 

var=”prop”  rows=”10″

preserveDataModel=”true” 

binding=”#{propertyMgtBeanAction.propServiceData}”> 


<t:column>

<f:facet name=”header”>

<t:commandSortHeader columnName=”propertyId”>

<h : outputText value=”Property Id” />

</t:commandSortHeader>

</f:facet>

<t:commandLink id=”cmlId” action=”#{propertyMgtBeanAction.editOrViewProperty}”>

<h : outputText value=”#{prop.propertyVO.idpk}” />

</t:commandLink>

</t:column>

………

……

</t:dataTable>

 

 

Here, I bind with JSF UIData instead of the DataModel component i.e. binding=”{propertyMgtBeanAction.propServiceData}”, the value shows collection of objects which are populated from an outside method execution. i.e. the getListOfPropertyServices() method does not contain any business logic. It simply returns an ArrayList of populated objects and this population is done by submitting the “search” button which invokes the method processSearchAction() in the managed bean. (I could achieve the similar goal by using the DataTable in which case value attraubue would take a DataModel object instead of a collection object). The code follows:

 

UI Layer: The managed bean:

package com.ns.web.properties;

import java.util.ArrayList;

import java.util.Date;

import java.util.List;

…….

import com.sun.facelets.FaceletContext;


@Component(“propertyMgtBeanAction”)

@Scope(“request”)

public class PropertyMgtBeanAction {

private UIData mPropServiceData; 

private List<PropertyServiceVO> mListOfPropertyServices = new ArrayList<PropertyServiceVO>();


@Autowired

@Qualifier(“iServiceManager”)

private IServiceManager mIServiceManager;

public String processSearchAction() {

……..

mListOfPropertyServices = mIServiceManager.findPropertyWithServicesBySearchCriteria(

mRequestTypeCheckbox.getSelectedValues(), 

mServiceCheckbox.getSelectedValues(), 

mStatusCheckbox.getSelectedValues(), 

dateFrom, dateTo, mPageNumber, Constants.DEFAULT_LIST_SIZE);

… … … 

…..

return null;

}


public List<PropertyServiceVO> getListOfPropertyServices() {

return mListOfPropertyServices;

}


public String editOrViewProperty() {

PropertyServiceVO ps = (PropertyServiceVO)mPropServiceData.getRowData(); 

mPdtoForTermsAgreement = mIPropertyManager.getPropertyDTO(ps.getPropertyVO().getIdpk());

String status = mPdtoForTermsAgreement.getJobStatus();

String tmp = “”;

if (status.equals(Constants.JOB_STATUS_NEW) || status.equals(Constants.JOB_STATUS_PENDING)) {

tmp = Constants.TERMS_OF_AGREEMENT;

}

return tmp;

}

}

 

What I wanted to show here is: when the “search” button is pressed the corresponding processAction method is invoked, and the ArrayList (listOfPropertyServices) is populated (called from managed bean -> service layer bean -> DAO layer bean) and the value attribute in <t:dataTable> simply gets a populated list. 

 

Now about the commandlink inside DataTable: As because we have used preserveDataModel=”true”, so clicking on the commandLink inside the DataTable will invoke the editOrViewProperty() in the managed bean. If we try to click on the link by setting preserveDataModel=”false”, then this will NOT work. This would simply behave like a normal JSF feature where the “DataTable would modify the id of the CommandLink during renderering, but the CommandLink does not know that it was rendererd with a different id” (refer to http://typo.ars-subtilior.com/articles/2007/02/07/jsf-datatable-and-commandlink)

 

Now some people argue that they don’t find any problem or, they never see this as a JSF TRAP. I’m afraid that they are probably putting business logics in their managed bean getter methods. E.G. they could write something like this:

 

public List<PropertyServiceVO> getListOfPropertyServices() {

mListOfPropertyServices = mIServiceManager.findPropertyWithServicesBySearchCriteria(

mRequestTypeCheckbox.getSelectedValues(), 

mServiceCheckbox.getSelectedValues(), 

mStatusCheckbox.getSelectedValues(), 

dateFrom, dateTo, mPageNumber, Constants.DEFAULT_LIST_SIZE);

return mListOfPropertyServices;

}

 

This is NOT a recommended approach. And this will work, however.

If this is the case, you don’t need preserveDataModel. In fact, you don’t need to use tomahawk at all. You can make this work with any JSF implementation you like. But the problem is ALREADY KNOWN. You know how many times you are calling you getter method, hence how many times your business layer in invoked, and how many times your database is hit … Just for a simple population of Arraylist for a single method invokation?

 

The Business/Service layer: Where the actual business logic calculation is done. Although not much relevant, but the business layer code here shown here for your convenience. 

package com.ns.business.services;

import java.util.ArrayList;

….

….

@Service(“iServiceManager”)

@Transactional(isolation = Isolation.REPEATABLE_READ)

public class ServiceManagerImpl extends BaseManagerImpl implements IServiceManager {

protected final Log log = LogFactory.getLog(getClass());

 

@Autowired

@Qualifier(“iPropertyServiceDao”)

private IPropertyServiceDao mIPropertyServiceDao;


public List<PropertyServiceVO> findPropertyWithServicesBySearchCriteria(final Object[] pRegPurpose, 

final Object[] pServices, final Object[] pStatus, final Date pFromDate, final Date pToDate, 

final int pSelectedPageIndex, final int pNumberOfRowsPerPage) {


// Some business logic to populate variables

List<String> tmpRegPurpose = ….

List<Long> serviceIds = ….

List<String> tmpStatus = ….

mTotalNumberofRows = … 

mMaxPageNumber = …. 

// Call DAO to fetch the result

return mIPropertyServiceDao.findPropertyServicesBySearchCriteria(tmpRegPurpose, serviceIds, 

tmpStatus, pFromDate, pToDate, firstResult, pNumberOfRowsPerPage);

}

}

 

The DAO Layer: Not relevant, hence not shown here.

Posted in: JEE

3 thoughts on “JSF: Using DataTable with nested CommandLink or, CommandButton

  1. Justin says:

    I am trying to use tomahawk t:dataScroller for pagination on my jsf page. Data is rendered properly for the first page but whenever I try to click any button to go to next page, java script error is thrown as below

    form is undefined
    var oldTarget = form.target;

    Any solution to this will be quite helpful.

    I am using tomahawk12-1.1.9 lib with JSF2.0

  2. GenMX says:

    While reading your blog it seems that you research on this topic very much. I must tell you that your blog is very informative and it helps other also..

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Google photo

You are commenting using your Google account. Log Out /  Change )

Twitter picture

You are commenting using your Twitter account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s