Add new attachment

Only authorized users are allowed to upload new attachments.

This page (revision-1) was last changed on 26-Mar-2007 15:04 by UnknownAuthor

Only authorized users are allowed to rename pages.

Only authorized users are allowed to delete pages.

Difference between version and

At line 1 added 123 lines
A very typical requirement is setting up a standard set of headers/footers etc. that surround every page in an application. This is done by setting up an outer page template, which in some frameworks is called a ''layout''. If you have some experience of building up [Producer|ComponentProducer] networks and [writing RSF components|BuildingRSFComponents], it will be no surprise to find that this is handled in RSF by the familiar means of setting up pure HTML templates with appropriate {{[rsf:id|IDs]}}s and arranging injection in Spring of the relevant producers.
In this case, most of the necessary Spring definitions are already set up within RSF, and it just a matter of rewiring them slightly. Let's start at the HTML templating level to get a clear overview of what is going on.
!! Outer page template example
This is a sample template, in this case taken from the [Hibernate Cookbook|HibernateCookBook] sample application, which defines the outer, standard part of the page, together with a [branch|UIBranchContainer] tag, here named {{page-replace:}}, indicating where the page body is to be replaced with the different views for each page on the site:
{{{
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns:rsf="http://ponder.org.uk/rsf">
<head>
<title>Online Cookbook</title>
</head>
<body>
<h1>Online Cookbook</h1>
<div rsf:id="page-replace:">
<!-- Page body will go here -->
</div>
<p>
<a href="recipe-edit.html" rsf:id="new-recipe">Create new recipe</a>
<a href="recipes.html" rsf:id="recipes-all">Show all recipes</a>
<a href="categories.html" rsf:id="categories-all">Show all categories</a>
</p>
</body>
</html>
}}}
Note that there is nothing "unusual" about this template - it is in fact indistinguishable from any normal RSF pure HTML template, obeying the same rules for any components [peering] with tags in it arranged by {{[rsf:id|IDs]}}.
!!NullaryProducer for the outer template
There is however a slight difference with the [producer|ComponentProducer] that is paired up with our page layout template - since this markup is expected to be produced for ''every'' view in the application, the associated producer is not a {{ViewComponentProducer}} as it is for view templates, but the much more slimline {{NullaryProducer}}:
{{{
public interface NullaryProducer {
public void fillComponents(UIContainer tofill);
}
}}}
{{NullaryProducer}} is in fact the simplest possible Producer interface, supplying no other context except the container to be filled. This makes sense, since in this case component production is beginning at the very root of the component tree, with no other context known to the framework.
The implementation of the producer backing the template above looks like this:
{{{
public class Layout implements NullaryProducer {
private NullaryProducer pageproducer;
public void setPageProducer(NullaryProducer pageproducer) {
this.pageproducer = pageproducer;
}
public void fillComponents(UIContainer tofill) {
UIInternalLink.make(tofill, "recipe-create",
new EntityCentredViewParameters(RecipeEdit.VIEW_ID, new EntityID("Recipe", "new 1"),
EntityCentredViewParameters.MODE_NEW));
UIInternalLink.make(tofill, "recipes-all", new SimpleViewParameters(
Recipes.VIEW_ID));
UIInternalLink.make(tofill, "categories-all", new SimpleViewParameters(
Categories.VIEW_ID));
UIJointContainer page = new UIJointContainer(tofill, "page-replace:", "page:");
// include the components from the page body into tag "page-replace:"
pageproducer.fillComponents(page);
}
}
}}}
Again, notice that it functions like any other RSF producer, generating components to match the {{rsf:id}}s of the tags in the template. Once it reaches the {{<div>}} where the page body is to be inserted, it must defer to the framework to fetch the correct component subtree for this view. It does this in a way extremely similar to that of an RSF [Evolver] which is the basis of the RSF "widget" system, by creating a [UIJointContainer] which forces the renderer to take a branch to another template - and then filling the container with the subtree obtained from the framework, via another {{NullaryProducer}} that is injected in.
In this case the client ID and joint IDs for the {{UIJointContainer}}, {{page-replace:}} and {{page:}} are at the discretion of the author. The value used for {{page-replace:}} must match the ID used in your outer page template, like the one above. The value used for {{page:}} must match the one used in your actual view pages (see below).
!!Spring definitions for outer producer and templates
Our producer implemention is registered with Spring in the normal way - it needs to acquire an extra dependency from the framework in order to fill its joint when required:
{{{
<bean id="pageProducer" class="uk.org.ponder.rsf.cookbook.producers.Layout">
<property name="pageProducer" ref="pageBasicProducer"/>
</bean>
}}}
{{pageBasicProducer}} is a standard RSF request-scope bean implementing the {{NullaryProducer}} interface, which will deliver all the components fetched from the ViewProducers for the current request, whatever they are. The name {{pageProducer}} for this bean is also fixed - giving your bean this name indicates that you are replacing the standard {{pageProducer}} which produces the ViewRoot to RSF with your own implementation. {{pageProducer}} must be declared at request scope.
Finally you need to register the HTML template file used for the outer layout with RSF, using the standard parent definition {{rootTemplateContributorParent}}:
{{{
<!-- Register the template "layout.html" as defining root layout for
each page in the application (extension is auto-inferred from view type),
base directory is defaulted from "defaultTemplatePath" -> content/templates/ -->
<bean parent="rootTemplateContributorParent">
<property name="templateNames" value="layout"/>
</bean>
}}}
This is very similar to the {{templateContributorParent}} used for [standard RSF UI components|BuildingRSFComponents]
!!Writing page templates in your application
The ViewProducers in your application are unchanged whether you are using outer page templates/layouts or not. The view templates are changed very slightly in that they must contain a branch tag enclosing the part of their markup which is intended to be included into the layout. For example, we set up our outer producer above to use the joint ID of {{page:}}, and this is the branch ID we must use in our view templates.
For example, here is the template for one of the views in our CookBook application:
{{{
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns:rsf="http://ponder.org.uk/rsf">
<head>
<title rsf:id="edit-title">Edit category</title>
</head>
<body>
<div rsf:id="page:">
<h1 rsf:id="new-category-heading">New category</h1>
<h1 rsf:id="edit-category-heading">Edit category</h1>
<!-- Etc., Rest of view template in here -->
</div>
</body>
</html>
}}}
We include enough header definitions to make this a valid XHTML document, but note that everything outside the tag {{<div rsf:id="page:">}} will be stripped out on rendering. The UIJointContainer {{= new UIJointContainer(tofill, "page-replace:", "page:");}} we issued in our outer producer will ''join'' the tag {{<div rsf:id="page-replace:">}} in the outer template to the tag {{<div rsf:id="page:">}} for whichever is the template for the current view.
Version Date Modified Size Author Changes ... Change note
26-Mar-2007 15:04 7.807 kB UnknownAuthor
« This page (revision-) was last changed on 26-Mar-2007 15:04 by UnknownAuthor