JSF: DataTable and CommandLink

Posted by Dominik Wed, 07 Feb 2007 13:39:00 GMT

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)

Posted in  | Tags  | 21 comments | no trackbacks

Comments

  1. Avatar Frank Fischer said about 4 hours later:

    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. Avatar evilmonkey said about 4 hours later:

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

  3. Avatar Saurav said about 5 hours later:

    good one……

  4. Avatar Dominik said about 20 hours later:

    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. Avatar Dominik said about 20 hours later:

    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. Avatar Réda said 2 days later:

    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. Avatar Dominik said 2 days later:

    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. Avatar tom said 24 days later:

    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. Avatar gvnbvnv said 2 months later:

    mbnmbn,b,b

  10. Avatar Peter said 2 months later:

    Thanks

  11. Avatar Samson said 3 months later:

    It is wonderful

  12. Avatar Wonderful said 3 months later:

    It is Samson

  13. Avatar chaitanya said about 1 year later:

    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. Avatar sam said about 1 year later:

    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. Avatar shakeel.abbas said about 1 year later:

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

  16. Avatar andres said about 1 year later:

    You can use the tag (from tomahawk) , in the same page where the table is. It works for me.

  17. Avatar andres said about 1 year later:

    the tag is saveState

  18. Avatar M Chisty said about 1 year later:

    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. Avatar M M Islam Chisty said about 1 year later:

    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. Avatar JSF beginner said about 1 year later:

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

  21. Avatar Same JSF beginner, few minutes later :) said about 1 year later:

    It seems I was using (tomahawk) and . 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.

Trackbacks

Use the following link to trackback from your own site:
http://www.ars-subtilior.com/typo/trackbacks?article_id=jsf-datatable-and-commandlink&day=07&month=02&year=2007

(leave url/email »)

   Comment Markup Help Preview comment