Matt RaibleMatt Raible is a Web Developer and Java Champion. Connect with him on LinkedIn.

The Angular Mini-Book The Angular Mini-Book is a guide to getting started with Angular. You'll learn how to develop a bare-bones application, test it, and deploy it. Then you'll move on to adding Bootstrap, Angular Material, continuous integration, and authentication.

Spring Boot is a popular framework for building REST APIs. You'll learn how to integrate Angular with Spring Boot and use security best practices like HTTPS and a content security policy.

For book updates, follow @angular_book on Twitter.

The JHipster Mini-Book The JHipster Mini-Book is a guide to getting started with hip technologies today: Angular, Bootstrap, and Spring Boot. All of these frameworks are wrapped up in an easy-to-use project called JHipster.

This book shows you how to build an app with JHipster, and guides you through the plethora of tools, techniques and options you can use. Furthermore, it explains the UI and API building blocks so you understand the underpinnings of your great application.

For book updates, follow @jhipster-book on Twitter.

10+ YEARS


Over 10 years ago, I wrote my first blog post. Since then, I've authored books, had kids, traveled the world, found Trish and blogged about it all.

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 in Java at Nov 28 2004, 10:02:25 AM MST 9 Comments
Comments:

Matt, you might try a redirect to the details page. It's just an empty element under the navigation case <redirect/>. But, I'm with you on the heavy use of javascript. I put togather several components for the data table that don't use javascript (Pager, Caption, Column Sorter, Conditional Link, Conditional Button). I tried to see if I could find a good open source site for them but it seems that most don't mind the heavy java script (https://ourfaces.dev.java.net/servlets/ProjectForumMessageView?messageID=4867&forumID=337) . I contributed these to the project but they were removed from the cvs so I just gave up. I'll send you what I have if your interested...

Posted by Gary VanMatre on November 28, 2004 at 05:42 PM MST #

Thanks for the tip Gary. I tried adding <redirect/> to the navigation-case for editing a user, but this just results in a blank form, rather than displaying the user's information.

    <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 #

Matt, You could try using the outputLink rather than the commandLink. The output link will generate a standard link. You might try adding a base tag. All JSF jsp must be dispached thur the faces servlet. It looks like your are using the /faces/* servlet mapping. I put togather a simple JSF component for this - like struts. I belive the the struts JSF baseline has one.

<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 #

How do you block xml? 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}" h: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

Posted by 67.176.118.130 on November 28, 2004 at 07:15 PM MST #

To block XML, just use HTML - like you normally would. You'll need to escape < with &amp;lt; and such. I use the E2 Source Code Formatter.

Posted by Matt Raible on November 28, 2004 at 07:32 PM MST #

Nice. Very cool utility.

Posted by 67.176.118.130 on November 28, 2004 at 08:50 PM MST #

Matt, >> 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 ? I'm not aware of anything built-in but we're using our custom version of <f:param for that. In every row of our table there's a link taking the user to details page. When it's clicked - the ID should be passed as an argument, so our link looks like:
<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 #

At least this helps in preventing accidental POST "redos". This is a nice feature. May be you can disable it by configuration ???

Posted by toto on December 01, 2004 at 01:17 PM MST #

I think my question was *almost* touched on here. The to-view-id element tells where a Faces app should go next. But can I go to another servlet?

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 #

Post a Comment:
  • HTML Syntax: Allowed