Handling Command components within table columns
In a typical master/detail scenario, a table displays a Collection of objects. There is a command component (a link or button) in each table row that performs some operation on the object for that row (for example, navigating to a separate page that shows more details about that object, or allows it to be edited).
The problem is that the command's "action" attribute points to the same backing bean method to run for each row. When the method is executed, it somehow needs to know which of the rows was actually selected.
Using f:setActionPropertyListener or t:updateActionListener
Note: The f:setActionPropertyListener tag was added in JSF1.2. For people using JSF1.1, the Tomahawk library provides t:updateActionListener which does the same thing.
This approach causes two things to happen when a command component is clicked:
1. store the command component in a property on the backing bean, then
2. run the "action method", which can look at that property to see exactly which item was selected
This solution looks something like the following:
When the above link is clicked, JSF will first call the setCurrentEmployee(Employee) method on the backing bean (which should store its parameter as a member on the backing bean). The prepareForEdit method can then just use that member to know which specific employee was chosen.
Using the table's DataModel
Each table component (h:dataTable) has a DataModel object, and this DataModel knows which is the "currently selected" row.
- An h:dataTable can be configured to use a !DataModel explicitly provided by your code, ie the "value" attribute of the h:dataTable can point to a backing-bean method that returns a !DataModel. If you are doing this, and the !DataModel object is cached in a member of your backing bean, then the "action method" that is triggered can simply access that member and call its getRowData() method to get the object for the "selected row".
- If the "value" attribute of the h:dataTable instead points to a backing-bean method that returns a List or similar collection, then the dataTable will internally create a !DataModel to wrap that list. You can access this DataModel instance by using the "actionListener" attribute on the command component, rather than the "action" one. The backing-bean method is then passed an ActionEvent object which contain a reference to the command-component that was clicked on; by walking up the component tree the enclosing UIData component can be found, and its DataModel then retrieved. Actually, it's even simpler, as the "getRowData" method on the UIData component can be used directly (it just delegates to the DataModel method).
The JSF page looks like this: And the backing bean looks like this:
- You could also use the "binding" attribute on the h:dataTable to make it accessable from the backing bean. However component bindings have their own problems and should be avoided where possible.
Passing params with f:param
If you are coming from Struts or some other servlet MVC framework you may have previously solved this problem by passing some kind of primary key as request parameters as part of a link, perhaps via something like this:
Using JSTL to create the above:
It is possible to use this approach with JSF, but it is not in the spirit of JSF; the solutions documented above are generally considered better. Nevertheless, this can be implemented as follows:
Use an f:param tag inside of the commandButton or commandLink:
To then get a handle to this request parameter in your "prepareEdit" method, you could do:
Note that in this case the parameter is a String, which you'll have to map to the appropriate object id yourself. Plus, you have extra lines of code just to get a handle to the map holding the parameters. Sure you can push that off to a utility class, but the above solutions are cleaner ways to handle this.