Making a component Layout with Tapestry 5 is really simple.
An example is given at: http://tapestry.apache.org/tapestry5/guide/layout.html in the official tapestry 5 documentation but I propose a more sophisticated component with content fallback, able for instance to override its left column content if needed.
We'll finish with something that will look like a bit like the Tiles Template system (Struts), but much much simpler than Tiles.
In the package components, create a Layout component and template
Layout.java :
import org.apache.tapestry.annotations.Parameter; import org.apache.tapestry.ioc.annotations.Inject; import org.apache.tapestry.services.Request; /** * @author Michael Courcy * * This component aims to provide layout service for a page * */ @IncludeStylesheet("context:layout.css") public class Layout { @Parameter private String title = "My Tapestry powered site"; @Inject private Request request; public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Request getRequest() { return request; } public void setRequest(Request request) { this.request = request; } }
Layout.tml
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>${title}</title> </head> <body> <table id="main"> <tr> <td id="header" colspan="2"> The header </td> </tr> <tr> <td id="left"> Here come the left content </td> <td id="content"> <t:body/> </td> </tr> <tr> <td id="footer" colspan="2"> Tapestry powered Site </td> </tr> </table> </body> </html>
Then add this css file in the root context of your webapp.
layout.css
table#main { width: 700px; margin-left:auto; margin-right:auto; } td#header { height: 120x; border: 1px solid blue; padding: 10px; margin: 10px; } td#footer { height: 120x; border: 1px solid blue; padding: 10px; margin: 10px; } td#left{ width: 200px; border: 1px solid red; padding: 10px; margin: 10px; } td#content{ border: 1px solid red; padding: 10px; margin: 10px; }
Let's try it! In the package page, create a page TestLayout.java which does ot do anything as its only purpose is to test the component.
public class TestLayout { }
and in the root context of your webapp, create the template TestLayout.tml :
<t:layout title="literal:My first use of layout component" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> My content </t:layout>
You should get this:
Note that the title of the page is passed as a parameter.
But could we regard this solution as fulfilling our requirements?
Not really, because the content of the left column is now static and we're about 100% sure that for some pages we'll have to override it.
Let's change the component using Delegate, Block, Parameter and a component of our own : Left.
Left.java (in the components package)
/** * @author Michael Courcy * * This component is just a content holder to fill the left column of the layout component * */ public class Left { }
Really simple, isn't it? That's barely a container.
Now let's change Layout.tml.
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> <head> <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" /> <title>${title}</title> <link href="${request.contextPath}/layout.css" rel="stylesheet" type="text/css" /> </head> <body> <table id="main"> <tr> <td id="header" colspan="2"> The header </td> </tr> <tr> <td id="left"> <t:delegate to="leftContent" /> <t:block> <div t:id="leftRegularContent"> Here come the left regular content </div> </t:block> </td> <td id="content"> <t:body/> </td> </tr> <tr> <td id="footer" colspan="2"> Tapestry powered Site </td> </tr> </table> </body> </html>
What you have to focus on is the change done in the left column, the content of the component leftRegularContent will be a "content fallback".
You have to change Layout.java as well
package org.apache.tutorial.tapestrySpringHibernate.components; import org.apache.tapestry.Block; import org.apache.tapestry.annotations.Component; import org.apache.tapestry.annotations.Parameter; import org.apache.tapestry.ioc.annotations.Inject; import org.apache.tapestry.services.Request; /** * @author Michael Courcy * * This component aims to create a layout around the main content * You can optionnaly defines left content that overrides the regular left * content if needed through the <t:parameter name="left">some overiding left content</t:parameter> * */ public class Layout { @Parameter private String title = "My Tapestry powered site"; @Parameter private Block left; @Component private Left leftRegularContent; @Inject private Request request; /** * This method check if the left parameter has been set by the user * if not the regular content is shown otherwise the content of this parameter * * @return the component we want to display */ public Object getLeftContent(){ return left==null ? leftRegularContent : left; } public String getTitle() { return title; } public void setTitle(String title) { this.title = title; } public Request getRequest() { return request; } public void setRequest(Request request) { this.request = request; } }
Et voila !! Without defining a left parameter :
<t:layout title="literal:My first use of layout component" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> My content </t:layout>
you get this:
With a left parameter defined :
<t:layout title="literal:My first use of layout component" xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd"> My content <t:parameter name="left"> Other left content </t:parameter> </t:layout>
You get this:
(originally posted on Michael Courcy's blog - the images only work if you right click and view image, if anyone can fix this or can host the images where they'll work inline - please do so )
NOTE: Inline images have now been fixed (11/23/2008)