At line 1 added 100 lines |
A [ViewParameters|https://saffron.caret.cam.ac.uk/svn/projects/RSFUtil/trunk/src/uk/org/ponder/rsf/viewstate/ViewParameters.java] object holds a deserialized (from the HttpServletRequest or similar) representation of the location of the view for the current request. In HTTP idiom, ViewParameters is a technology-neutral representation of a URL. |
|
!!ViewParameters fields |
There are various housekeeping fields defined in the base class of ViewParameters, most of which needn't concern the developer, apart from the core field [viewID], which is the central index in RSF that defines the identity of a view. |
|
{{{ |
public String viewID; |
}}} |
|
Each view in RSF is associated with exactly one specific subclass of ViewParameters. |
Unless the [ViewProducer|ComponentProducer] defines otherwise, the class which will be used is SimpleViewParameters, which defines no extra fields, and uses a straightforward URL representation strategy with the viewID parsed from the first component of the URL. |
|
!!Starting with SimpleViewParameters |
For example, in the [LogonTest|LogonTest_2] application, we saw this line: |
|
{{{ |
UIInternalLink.make(pane, "waleslink", new |
SimpleViewParameters(WalesFrameProducer.VIEW_ID)); |
}}} |
|
This indicates that the view {{walesframe}} is using the default, SimpleViewParameters, which means that inside the {{fillComponents}} method, the object {{viewparams}} delivered to it would be of this class. However, since there is no extra information other than the viewID (which we already know, it would have to be our own) inside this params, there would be no reason to inspect them inside the producer. |
|
!! Defining your own ViewParameters |
|
Most applications will want to pass user-defined view state from view to view via links - in fact RSF's main strength is its idiomatic and natural support for URL state. In order to define what this extra view state is, the developer will need to derive their own subclass from ViewParameters (one of the few cases where subclasses are used within RSF). In fact it is generally most convenient to derive from SimpleViewParameters rather than ViewParameters itself, since there is special support for setting up a default set of URL parsing rules for SimpleViewParameters subclasses. So, for example, we could write: |
|
{{{ |
public class NavParams extends SimpleViewParameters { |
public String itemID; |
public String viewtype; |
public int pageseq; |
|
public String keywords; |
} |
}}} |
|
Note that the user really needs to do very little other than list the extra fields one wants in a structure (or [Pea]) - RSF will take on the rest of the work of implementing the parsing system, as well as the {{copyBase()}} method which is essential for getting the full power of [RESTful flows|Flows#RESTfulFlows]. You may want to implement one or more convenience constructors, though. |
|
Now all we need to do is to register with RSF that this ViewParameters class will be the one used for a particular view. This is done by implementing the ViewParamsReporter interface from the view's producer, which for example would look like this: |
|
{{{ |
public class NavFrameProducer implements ViewComponentProducer, ViewParamsReporter { |
public static final String VIEWID = "nav-frame"; |
public String getViewID() { |
return VIEWID; |
} |
|
public void fillComponents(UIContainer tofill, ViewParameters origviewparams, |
ComponentChecker checker) { |
|
NavParams navparams = (NavParams) origviewparams; |
// use the navparams here when generating the view contents |
... |
} |
|
public ViewParameters getViewParameters() { |
return new NavParams(); |
} |
}}} |
|
Note that the getViewParameters() method simply returns an ''exemplar'' for the kind of ViewParameters that required - there is no need to initialise any of the fields on it, since RSF is really only interested in its type. |
|
So, finally, any other producer wanting to make a link to this view might for example |
use code like the following: |
{{{ |
NavParams navparams = new NavParams(); |
navparams.itemID = hit.get("identifier"); |
navparams.pageseq = Integer.parseInt(pageno); |
navparams.viewID = NavFrameProducer.VIEWID; |
navparams.keywords = freetext; |
|
UIInternalLink.make(hitrow, "identifier-link", hit |
.get("identifier"), navparams); |
}}} |
|
!!ParseSpecs |
|
By default, any ViewParameters derived from SimpleViewParameters will have a [ParseSpec] (or "parse specification") deduced from its fields automatically. The default parseSpec maps every bean property (or public field) into a URL attribute of the same name. To take control of this strategy you can override the {{getParseSpec}} method to map the ViewParameters state in a custom way to provide more "cool URLs". If you place more than simple leaf types into viewaparams (that is, other than simple Strings, String[]s or numbers) you will need to write a ParseSpec. |
|
!!!ViewParameters theory |
The implementation of the view parameters object is one of the primary responsibilities of the RSF application developer. In theory, the viewparams may be derived from the request in arbitrary ways, although in practice there is particular support for deriving public "leaf-type" fields of the form above from URL attributes of the same names (this will happen automatically if they are reported from the {{getAttributeFields}} method of ViewParameters). All Java primitive types are usable, as well as some composite ones, although URL considerations will probably suggest towards the use of simple types. |
|
The bulk of the other fields in ViewParameters are required for housekeeping for [flow|Flows] and error state, being the {{flowtoken}} and {{endflow}} indicator for flows, and the {{errortoken}} and {{errorredirect}} indicator for errors. The tokens collectively comprise the [view tokens|ViewToken], discussed somewhat in another page. |
|
!!Role of ViewParameters |
ViewParameters is critical in blocking off the last piece of dependence of RSF application code on the servlet infrastructure. To RSF, the ViewParameters are abstract tokens that are passed around and have their [viewID] field used as indexes to locate the [ViewTemplate] and [ComponentProducer]. To code ''within'' the [ComponentProducer], ViewParameters may well have a much richer structure, encoding many more details of flow and application state. |
|
In essence the ViewParameters representations bound the structure and possible flow paths through the application, by defining the unique representation of every view state. |
|
For common links ({{<a href="}} in HTML language), the target of a link is specified by the [ComponentProducer] when it emits the [UILink] object peering with the links. For submission links (resulting in POSTs) represented by [UICommand] components, the resulting ViewParameters is determined by the result of the [ActionResultInterpreter] registered by the application, which in essence defines the ''flow'' of the application by inspecting the result (if any) of the [Action] invokation as well as any errors that may have occured to compute the required ViewParameters for the client redirect that is (always!) issued. |
|
ViewParameters objects are frequently cloned and modified slightly during the course of the actions of a [ComponentProducer], since links leading from pages frequently lead to navigational state closely related to the source page. The {{copyBase()}} method is provided for this purpose. |
|
!!Future of ViewParameters |
ViewParameters are currently capable of dealing with serialisation of a bounded collection of leaf types within its object tree (Strings, Integers, Longs etc.). Before 1.0, the ViewParameters system will be upgraded to support unbounded collections (Lists, Maps, etc.) to allow use cases such as a dynamically sized set of sorting table controls within a view, etc. |
|
!! Potential pitfalls |
|
* Make sure your producer implements ViewParamsReporter! Defining getViewParameters() will appear to succeed but will never get called unless you implement this interface (naturally). If you are not getting the ViewParameters you expect, this is a good place to check. |
* If you override getParseSpec() remember to prepend your string with the return String of the supertype. |