ExecutingMethodsFromLinkButtonParameters
ExecutingMethodsFromLinkButtonParameters
Execute a managed bean method based on parameters provided for a link or button, or clicking on link/button on row to get to detail screen from master list
A typical master/detail scenario:
A table displays a Collection of objects. You want be able to click on an 'edit' link or button and be brought to the detail screen for editing the record.
If you are coming from Struts or some other servlet MVC framework your first thought would be to pass some kind of primary key as request parameters as part of a link:
<a href="/appContext/someAction.do?id=1234&userAction=prepareEdit">Edit</a>
Using JSTL to create the above:
<c:url value="someAction.do" var="url"> <c:param name="id" value="1234" /> <c:param name="userAction" value="prepareEdit" /> </c:url> <a href="${url}">Edit</a>
With MyFaces, there are several ways to handle this. Below are three ways. Note that there are disputable reasons to not to use the first way, but it is shown here as it as it might be the first one you are inclined to try.
0) Using <f:actionListener ...> and getRowData() of UIData
There is a very easy way to get from an ActionListener to the row bean without using a parameter. Please have a look at Working with DataTable and ActionListeners.
1) Passing params with f:param
The first thing you might think of doing with JSF is to mimick the typical way you've passed parameters in a link. To do this with JSF, you could use the f:param tag inside of commandButton or commandLink:
<t:dataTable var="emp" .... > <h:commandLink id="editLink" action="#{employeeAction.prepareEdit}"> <h:outputText value="#{msg.edit}"/> <f:param name="id" value="#{emp.id}"/> </h:commandLink>
To then get a handle to this request parameter in your "prepareEdit" method, you could do:
FacesContext context = FacesContext.getCurrentInstance(); Map map = context.getExternalContext().getRequestParameterMap(); String employeeID = (String) map.get("id");
The above will work, but I'm not too fond of this approach. For one, you'll be dealing with a String and you'll have to do conversions 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 there is a cleaner way to handle this.
2) Using <t:updateActionListener ... />
Before I get to using the tag, some background information...
Typically your backing bean (continuing with the example above, EmployeeAction.java) will have either 1) A member property that is a bean that will hold info you want to capture (often just a typical POJO value object) or 2) Direct memember properties in the backing bean with getters/setters
For example EmployeeAction might have:
private Employee employee; //standard POJO Value Object with properties name, id, etc setEmployee( Employeee emp ) // getEmployee() // or String name; //getName/setName Integer id; //getId/setId
both techniques are valid although I like the first approach. Since I usually have ValueObjects that are used in different places, it makes sense to me to just make the POJO a managed property of your backing bean. The only caveat is that you'll usually need to create a managed bean reference to your property in your faces-config.
ie:
<managed-bean> <managed-bean-name>employeeAction</managed-bean-name> <managed-bean-class>net.reumann.EmployeeAction</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>employee</property-name> <value>#{employee}</value> </managed-property> </managed-bean>
The above will make sure that EmployeeAction always has an instance of Employee set.
Now to use the t:updateActionListener tag you could just do:
-
<t:dataTable var="emp" .... > <t:commandLink action="#{employeeAction.prepareForEdit}"> <h:outputText value="#{msgs.edit}" /> <t:updateActionListener property="#{employeeAction.employee.id}" value="#{emp.id}" /> </t:commandLink>
When the above link is clicked it will set the id of the Employee object in our EmployeeAction bean.
If you were using id directly as a property in your EmployeeAction, the link would look like...
<t:commandLink action="#{employeeAction.prepareForEdit}"> <h:outputText value="#{msgs.edit}" /> <t:updateActionListener property="#{employeeAction.id}" value="#{emp.id}" /> </t:commandLink>
I really like this approach. It's clean and ex-Struts users can think of it as sort of like having your ActionForm properties set based on any request parameters submited. There is no need to have to get a handle to the Map backing the Request parameters like there is in the first approach.
3) Getting handle to DataModel Row.
Another approach if you are using a DataTable backed by a DataModel is to use the DataModel to get a handle to the object that is backing the row. So if the user clicks on the "edit" link on the third row, you can get a handle to the Employee object that backs that row in your EmployeeAction. The edit link would be simple...
<t:dataTable var="emp" value="#{employeesListBean.employeesModel}" ... > <t:commandLink action="#{employeeAction.prepareForEdit}" value="#{msgs.edit}"/>
In prepareForEdit we could get a handle to the Employee of that row....
employee = (Employee)getEmployeesListBean().getEmployeesModel().getRowData();
Note, our DataModel in this case happens to be created in a class called "EmployeeListBean" which is managed property of our EmployeeAction...
<managed-bean> <managed-bean-name>employeeAction</managed-bean-name> <managed-bean-class>net.reumann.EmployeeAction</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>employee</property-name> <value>#{employee}</value> </managed-property> <managed-property> <property-name>employeesListBean</property-name> <value>#{employeesListBean}</value> </managed-property> </managed-bean>
You could of course create your DataModel directly in the EmployeeAction class, in which case you wouldn't need the extra class (EmployeeListBean) holding the DataModel (I like to have it separate though in case other classes want to use it).
The other thing to note is that if your DataModel for the form doesn't have Session scope, you should add preserveDataModel="true" to your dataTable definition:
<t:dataTable var="emp" value="#{employeesListBean.employeesModel}" preserveDataModel="true" ...
This will make sure the same DataModel is present when you click on your edit link.
Another option is to use t:saveState to preserve your DataModel. In this example we would add t:saveState some place after f:view:
<f:view> <t:saveState id="employeesListBean" value="#{employeesListBean}"/>
last edited 2006-09-18 15:48:02 by MikeKienenberger