JSF: DataTable and CommandLink

Like a lot of other programmers who are forced to work with JSF, I fell into THE TRAP!

If you are using ManagedBeans in request scope, you get problems with CommandLinks inside DataTables.

DataTables are one thing I really like about JSF, and CommandLinks often come in handy as well. But when you put a CommandLink inside a DataTable, e. g., to select the entry of the row in which the CommandLink is, you get bitten. That is, if you want ManagedBeans with request scope. The action which should be triggered by the CommandLink is never triggered, the page is simply rendered again.

The reason for this behaviour is that the DataTable modifies the id of the CommandLink during renderering, but the CommandLink does not know that it was rendererd with a different id. During the decoding of the request which was triggered by clicking the CommandLink, the ComandLinkRenderer looks at a hidden form parameter. If the value of that form parameter equals the id of the CommandLink, an action is queued. If not, nothing is done.

Since the DataTable changes the ids, the value of the hidden form parameter does not match the id of the CommandLink.

So I wrote my own CommandLinkRenderer, overwrote the decode method and patched it so that the id match up again. When I want a CommandLink in a DataTable, I then specify that it should use my CommandLinkRenderer.

Here is the source of the decode method:


     @Override
    public void decode(FacesContext context, UIComponent component)
    {
        if (context == null || component == null)
        {
            throw new NullPointerException(Util
                    .getExceptionMessageString(Util.NULL_PARAMETERS_ERROR_MESSAGE_ID));
        }

        if (log.isTraceEnabled())
        {
            log.trace("Begin decoding component " + component.getId());
        }

        UICommand command = (UICommand) component;

        // If the component is disabled, do not change the value of the
        // component, since its state cannot be changed.
        if (Util.componentIsDisabledOnReadonly(component))
        {
            if (log.isTraceEnabled())
            {
                log.trace("No decoding necessary since the component "
                        + component.getId() + " is disabled");
            }
            return;
        }

        String clientId = command.getClientId(context), paramName = getHiddenFieldName(
                context, command);
        Map requestParameterMap = context.getExternalContext()
                .getRequestParameterMap();
        String value = (String) requestParameterMap.get(paramName);
        clientId = clientId.substring(0, clientId.lastIndexOf(":"));
        value = value.substring(0, value.lastIndexOf(":"));
        if (value == null || value.equals("") || !clientId.equals(value))
        {
            return;
        }
        ActionEvent actionEvent = new ActionEvent(component);
        component.queueEvent(actionEvent);

        if (log.isDebugEnabled())
        {
            log.debug("This command resulted in form submission "
                    + " ActionEvent queued " + actionEvent);
        }
        if (log.isTraceEnabled())
        {
            log.trace("End decoding component " + component.getId());
        }
        return;
    }

The diff to the original method is:


diff CommandLinkRenderer.java PatchedCommandLinkRenderer.java
124a125,126
>       clientId = clientId.substring(0, clientId.lastIndexOf(":"));
>       value = value.substring(0, value.lastIndexOf(":"));

It works form me, maybe others get some benefit from it as well.

(P.S. this is crossposted to http://forum.java.sun.com/thread.jspa?threadID=5116147)

Wed, 07 Feb 2007 07:39 Posted in

Tags

  1. By Frank Fischer 07/02/2007 at 11h28


    Can you be a bit more specific on the environment you are working in, espacially concerning the JSF implementation (myfaces, RI)? I never encountered the described “trap”, but quiet frankly this may be because everytime I use dataTables I work a) with statefull managed beans (session scope or Seam/EJB3 conversational Beans or SFSB) or b) use a request-parameter to submit the identifier of the row.

  2. By evilmonkey 07/02/2007 at 11h45


    you’re not “Valid XHTML 1.0”, click on the link to test your page :-)

  3. By Saurav 07/02/2007 at 12h11


    good one……

  4. By Dominik 08/02/2007 at 03h26


    Hi Frank,

    to be more specific:

    I’m currently using:

    • jsf-1.1_02
    • facelets-1.1.11

    I know that this doesn’t happen with statefull managed beans, but currently we try to get along stateless. Passing the request parameter of the row (or the identifier of the object in the row) works equally well, it is just that I needed a post request, because the action will remove an item from a list. I tried passing a parameter directly, but the invoke application phase is not run when no action was detected. So the workaround was to simply make CommandLink work inside a DataTable with request scoped managed beans.

  5. By Dominik 08/02/2007 at 03h29


    evilmonkey:

    you got me there. It’s true that this page is not valid xhtml :-[ I’ll fix it, that’s a promise. It’s just one “onClick” attribute.

  6. By Réda 09/02/2007 at 10h06


    Excuse me, but i don’t see the implemention of getHiddenFieldName() methode, because i tested this code and it make an compile error on this method. help please.

  7. By Dominik 09/02/2007 at 10h59


    Hi Réda,

    getHiddenFieldName() is a protected method on CommandLinkRenderer. If you extend CommandLinkRenderer it should be visible. The workaround is for the reference implementation of Sun, maybe other render kits don’t provide that method.

  8. By tom 03/03/2007 at 03h57


    hi all, in my application the commandlink, actionlistener and datatable work together in a request-scope bean when i use the attribute

    preserveDataModel=”true”

    of the datatable

    good luck!

  9. By gvnbvnv 10/04/2007 at 03h15


    mbnmbn,b,b

  10. By Peter 20/04/2007 at 09h32


    Thanks

  11. By Samson 29/04/2007 at 07h21


    It is wonderful

  12. By Wonderful 30/04/2007 at 09h07


    It is Samson

  13. By chaitanya 27/05/2008 at 04h13


    hi i have an where i am using tiles frame work,jsf ..i was displaying some links in menu , when i try to click on the any link it has to do some action …. here i used comandlinks where as that to be tiles frame work

  14. By sam 12/08/2008 at 08h34


    How can i overwrite CommandLinkRender decode method (jsf sun ri 1.1_02)??? Is there any method without building all the jsf ri sources????

  15. By shakeel.abbas 20/08/2008 at 06h55


    i am trapped in this problem and i found no solution to that the method you suggested where it may be placed

  16. By andres 25/09/2008 at 11h04


    You can use the tag (from tomahawk) <t:saveState value=”#{yourBeanName}”>, in the same page where the table is. It works for me.

  17. By andres 25/09/2008 at 11h05


    the tag is saveState

  18. By M Chisty 12/12/2008 at 23h32


    Hi Dominik, Could you please upload the corresponding source codes in this blog? i.e. the Java file source, how did you configure it to work with the commandlink etc?

    Thanks, … M. Chisty

  19. By M M Islam Chisty 13/12/2008 at 21h51


    Hi, Why cannot we use something like this:

    public class UserListBean {
    private UserService userService;
    prviate List users;
    public List getUsers() {
    if(users == null){
    users = userService.findAll();
    }
    return users;
    }
    }

    What is wrong if written this way? (i.e. checking if null before calling the business method that communicates with DAO layer). In this wayt, does not matter how many times getter is invoked, it will not trigger the ‘userService.findAll()’ method unless it finds a NULL. What do you think? Clarify please.

    … Chisty

  20. By JSF beginner 26/12/2008 at 11h03


    I am using MyFaces 1.1 and session scoped beans and have the same problem. I.E. the action method is not called.

  21. By Same JSF beginner, few minutes later :) 26/12/2008 at 11h17


    It seems I was using <t:dataTable> (tomahawk) and <h:commandLink>. Also I previously forgot to set the id attribute for the commandLink. So, using session scoped beans and the id and immediate attributes for commandLink fixed the problem.

  22. By vijaykrishnan@infinite.com 22/01/2010 at 04h56


    If i have an inputtext inside commandlink how do i disable command link alone. Commandlink is placed in an editabletable. If my editable table goes on edit mode the textbox is displayed with a link in it. I want to disable this. Kindly suggest me..

  23. By Brian Sternari 11/02/2010 at 22h18


    I had the same problem. Usually the problem of using h:commandLink within an h:datatable is that we include our h:datatable in a <h:form></h:form> (because h: commandLink works within a form), fine, but the h:datatable that we are developing is included in another JSP, and within another <h:form></h:form>.

    Another case is that we have the ManagedBean declared in faces-config with request scope, and that can be solved by changing the scope to session.

    In my case, I had a jsp:include (including my h:datatable with h:commandLinks in) within a <h:form></h:form>, the scope of the ManagedBean was session. So I just changed the structure of my JSP to not nest forms.

    Remember, h:commandLinks submits the form in which they are included.

    Sorry for my English. Greetings.

  24. By David Levy 30/11/2010 at 17h39


    Thanks for the blog… but dude, the Red!! I get migraines and this is almost a trigger (not joking)!!

Comment JSF: DataTable and CommandLink


RSS