GET forms#
Its idiomatic and coherent support for HTTP GET forms, most often seen on the web in search queries or other lightweight submissions, is one of RSF's greater architectural successes. It's worth noting that in many other frameworks (such as JSF, and others with a weak idiom for URL state), GET form support is a plain impossibility. Why try to move on to Web 2.0 before you even have an answer for 1.0?
Before reading further, developers may wish to refresh their memory with the following readable and thorough guide from Jukka Korpela, on the situations where the user semantic makes the use of GET forms appropriate.
The key concept to grasp when dealing with RSF GET forms, is that whereas a POST form is considered a submission against the bean model, a GET form is a submission against the URL state, which in RSF is represented by the ViewParameters object for the target view. RSF has a very coherent approach where all the standard form components (UIInput, UISelect, etc.) are used in just the same way, whereas whilst with a POST form the EL bindings were references into the request context, with a GET form, the bindings are instead references into the target ViewParameters object.
Setting up a GET form submission#
Two things are required to set up a GET form.
1. Constructing the form component#
Firstly, in the producer, one must construct a UIForm object using the correct 3-argument constructor:/** Creates an "unmanaged" GET form, targetted at the supplied ViewParameters */ public static UIForm make(UIContainer parent, String ID, ViewParameters viewparams)
Here is a sample call:
UIForm resolveform = UIForm.make(tofill, "resolve-form", new ResolveViewParams(targetresource));
In the HTML template, this peers with a <form> tag as follows:
<form rsf:id="resolve-form" method="get" action="resolve-permission.html">
(note that the action definition has been inserted in the template only for previewability, it will be overwritten when the form is rendered).
2. Issuing components for the form#
In the above example, the ViewParameters, ResolveViewParams , for the target view were defined (schematically) as follows:public class ResolveViewParams extends SimpleViewParameters { public String resourceid; public String permissionname; public String agentid; public ResolveViewParams(ResourceID resourceID) { this.viewID = CoreViews.RESOLVE_VIEW; this.targetID = resourceID; }
We see that the URL state was initialised with the core information defining the identity of the target view and some other relevant state, and the other ViewParams fields were left blank. The remaining ViewParams fields are left empty, ready to accept their values from the form components at runtime[1]. In this example we fill in these pieces of URL state with two components, a selection control and a text input box:
String[] permissions = permrenderer.getMessagePermissions().allpermissions .toStringArray(); UISelect.make(resolveform, "permission-name-control", permissions, labels, Permission.READ_PERMISSION, "#{permissionname}"); String currentuser = UserThreadMap.instance().getAuthenticatedUser(); UIInput.make(resolveform, "agent-id-field", "#{agentid}", currentuser);
So note that in this example there is no difference in the components we issue than there would be if this were a POST form, only the EL bindings refer to a different container. The bindings are referred to whichever ViewParameters object will be constructed in the target view, in this case the ResolveViewParams, and so the EL reference #{permissionname} binds the submission from the selection control onto the ResolveViewParams field permissionname, and similarly for #{agentid }[2],[3].
Role of GET forms within flows#
The strategy for GET forms emphasises the role of the ViewParameters object in RSF as a "mini-bean container" - it is a (much smaller) tree of essentially framework-free objects, much like the request context itself. This view makes RESTful Flows very idiomatic to support in RSF - the URL state for the current view can be supplied again as the Form target, and some additions/modifications supplied by issuing a few GET form components that tweak the URL state to result in a new view "just like the current one, but moved along a little". RESTful flows when used appropriately offer a superior user experience with full bookmarkability and economise on server resources and coding effort.Note that in many cases RESTful flows can simply be implemented with plain (though computed at render-time) links (UILink) without any form required at all, if no user adjustment other than the plain selection is required at the flow step - this is a very clear and powerful design strategy that is often overlooked by designers coming from other frameworks where POST submissions against a session state are the norm.
[#2] Note that the #{...} escaping characters are actually unnecessary in RSF EL expressions, but they can be retained for clarity - in an expression with many strings it can be useful to have the EL visually distinguished.
[#3] The strategy used prior to RSF 0.7.1 was to issue GET form components without bindings, and simply rely on a correspondence between the rsf:id of the component and the ViewParameters member name. This approach is still supported as a fallback, where no binding is supplied, but is not recommended as a result of the coupling that it produces between the bean model and the template naming. The 0.7.1 approach described on this page as well as being architecturally superior, also puts GET forms on an even footing with POST forms by allowing arbitrary EL expressions within the ViewParams "container" to be addressible.