Edit Screens with JSF
I'm working with JSF this morning and I'm finding one thing particularly annoying. I'm working on a simple master/detail screen and I'm tweaking the detail screen to fit my needs. It's just a <form> with some form elements. I change something, run "ant deploy-web" and hit "refresh" to see my updated page. Since everything in JSF is a post, I get prompted to re-submit the form. Fine, I agree - then I'm returned to the list screen. Argggh - why can't I just view the form again?! This might just be a MyFaces nuance, I'm not sure. Anyone know of a workaround?
Wanna see the bug/feature in action? Go to http://demo.raibledesigns.com/equinox-jsf/userList.html, click on a row - and after the edit screen displays, hit refresh on your browser. In an ideal world, you'd see the form again, but nope - you get the list instead.


Posted by Gary VanMatre on November 28, 2004 at 05:42 PM MST #
<navigation-rule> <from-view-id>/users.jsp</from-view-id> <navigation-case> <from-outcome>add</from-outcome> <to-view-id>/userProfile.jsp</to-view-id> <redirect/> </navigation-case> <navigation-case> <from-outcome>success</from-outcome> <to-view-id>/userProfile.jsp</to-view-id> <redirect/> </navigation-case> </navigation-rule>My edit() method has the following to get the username from the request.
FacesContext.getCurrentInstance().getExternalContext() .getRequestParameterMap().get("username");Do I have to use something else to get the "username" parameter, or is there a way to put the parameter into the "to-view-id" element?
Posted by Matt Raible on November 28, 2004 at 06:22 PM MST #
<h:dataTable value="#{viewcontroller.persons}" var="e" > ... <h:outputLink value="userProfile.jsp"> <f:param id="username" name="username" value="#{e.username}" /> <h:outputText value="#{e.username}" /> </sh:outputLink>You might try using an EL in the managed bean for your detail page.
<managed-bean> <managed-bean-name>adduser</managed-bean-name> <managed-bean-class>xxx.User</managed-bean-class> <managed-bean-scope>request</managed-bean-scope> <managed-property> <property-name>username</property-name> <value>#{param['username']}</value> </managed-property> </managed-bean>... or ....
I might handle on the details page using the prerender callback of the script collector
<hx:scriptCollector preRender="#{viewcontroller.load}"> public void load(FacesContext context) { // it looks like Shale is going to have a ViewController and BeanMapper to handle this kind of thing? ValueBinding valuebinding = context.getApplication().createValueBinding("#{addUser}"); xxx.User user = (User) valuebinding.getValue(context); String userid = (String) context.getRequestParameterMap().get("username"); user.setUserid(userId); }Base tag render example....
public class OutputBaseRenderer extends HtmlBasicRenderer { public void encodeEnd(FacesContext context, UIComponent uicomponent) throws IOException { ResponseWriter writer = context.getResponseWriter(); writer.write("<base href=\""); writer.write(getActionURL(context, (OutputBase) uicomponent)); writer.write("\">"); writer = null; } protected String getActionURL( FacesContext context, OutputBase uicomponent) { String uri = context.getViewRoot().getViewId(); HttpServletRequest request = (HttpServletRequest) context.getExternalContext().getRequest(); StringBuffer url = new StringBuffer(); url.append(request.getRequestURL()); // remove the Schema for (int i = 4; i < 5; i++ ) { if (url.charAt(i) == ':') { url.delete(0, i); break; } } // remove all but the server name for (int i = 3; i < url.length(); i++ ) { if ((url.charAt(i) == ':') || (url.charAt(i) == '/')) { url.setLength(i); break; } } if (uicomponent.isSecure()) { url.insert(0, "https"); } else { url.insert(0, "http"); } url.append(":").append(uicomponent.getServerPort()); url.append(context.getApplication().getViewHandler().getActionURL( context, uri)); uri = null; request = null; return url.toString(); } }Posted by Gary VanMatre on November 28, 2004 at 07:10 PM MST #
Posted by 67.176.118.130 on November 28, 2004 at 07:15 PM MST #
Posted by Matt Raible on November 28, 2004 at 07:32 PM MST #
Posted by 67.176.118.130 on November 28, 2004 at 08:50 PM MST #
<h:dataTable var="row" ... > ... <h:column> <h:commandLink action="some_screen" immediate="true"> <h:outputText value="yada-yada/> <xyz:param name="id" value="#{row.id}" copyTo="#{ContextBean.id}"/> </h:commandLink> </h:column> ... </h:dataTable>Since our custom ParamComponent extends UIParameter - the default implementation of <h:commandLink will add ID's value as an argument but we don't have to pull it from request params and store somewhere when request arrives - 'copyTo' attribute does that for us.public class ParamComponent extends UIParameter { public void decode(FacesContext context) { super.decode(context); ValueBinding copyTo = getValueBinding("copyTo"); if ( copyTo == null ) { return; } String name = getName(); Object value = context.getExternalContext(). getRequestParameterMap().get(name); if ( value == null ) { return; } copyTo.setValue( context, value ); } ... }That's it: the details page can now refer "#{ContextBean.id}" to see the ID. The ContextBean is managed bean holding all params (ID is just one of them) that may be required to "pass" from one page to another using custom <xyz:param version. It may be request-scoped but then one has to take care of *all* cases when flow comes to details page and do not forget to add this parameter. It may be session-scoped (like we did) which makes life easier and always keeps the last "pass ID" (ID which passed from page to page) for us.Posted by Genie on November 30, 2004 at 08:37 AM MST #
Posted by toto on December 01, 2004 at 01:17 PM MST #
In certain circumstances Struts chaining is useful. (The commenting mechanism makes it hard to insert the XML here.) If the OK result from page1.jsp is found the next forward action can be to servlet identified as preparePage2. An OK result from that servlet can cause page2.jsp to be emitted. Note that I assume I don't care about the Struts repopulation problem because some other page (and ActionForm) is being created.
Without this preparePage2 servlet the page2.jsp page must prepare its own database structures, or the action handling the POST of page1.jsp must know enough about navigation to prepare page2.
So, if I want to have this behavior in Faces can I convince to-view-id to go to a servlet destination? I've seen no examples of to-view-id that *didn't* involve a .jsp reference.
Posted by Jerome Mrozak on January 05, 2005 at 09:02 PM MST #