You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@tapestry.apache.org by Apache Wiki <wi...@apache.org> on 2007/06/11 13:13:44 UTC

[Tapestry Wiki] Update of "Tapestry5HowToCreateTabPanel" by ErikVullings

Dear Wiki user,

You have subscribed to a wiki page or wiki category on "Tapestry Wiki" for change notification.

The following page has been changed by ErikVullings:
http://wiki.apache.org/tapestry/Tapestry5HowToCreateTabPanel

The comment on the change is:
Attribution goes to Kris Marinkovic, who explained the process to me

New page:
I wanted to create a tab panel in T5, and Kris Marinkovic was so nice to explain it to me in his email on June 11, 2007 (in the tapestry users email list). It's actually quite simple to do, and I've learned some new concepts from it too, so let's get to it.

Assume you have created a Tab component (e.g. declared in org.example.myapp.components). I'll keep it simple, and this component's Tab.html isn't there (so there is no template), and the Tab.java only contains a default setter to display a message.
{{{
package org.example.myapp.components;

import org.apache.tapestry.MarkupWriter;
import org.apache.tapestry.annotations.BeginRender;

public class Tab {
	private String message="Bonjour from HelloWorld component.";
	
	@BeginRender
	void renderMessage(MarkupWriter writer) {
		writer.write(message);
	}

	public void setMessage(String message) {
		this.message = message;
	}
}
}}}

Now that you have a component to display the tab panel's content, you need a way to select it. Let's assume your Start page needs to contain the tab panel. To make it more interesting, I've used Yahoo's YUI library to create a header/footer/secondary/main page layout. So here is your Start.html template file:
{{{
<html xmlns:t="http://tapestry.apache.org/schema/tapestry_5_0_0.xsd">
<head>
<title>Start Page</title>
<!-- Load all necesary javascript files and style sheets -->
<link rel="stylesheet" href="css/reset-fonts-grids.css" type="text/css"></link>
<link rel="stylesheet" href="css/styles.css" type="text/css"></link>
</head>
<body>
<div id="doc" class="yui-t2">
<div id="hd">
<p align="center">
Header placeholder
</p>
<p align="right">
	<t:actionlink t:id="tab1_link" title="My first tab">Tab1</t:actionlink>
	<t:actionlink t:id="tab2_link" title="My second tab">Tab2</t:actionlink>
	<t:actionlink t:id="tab3_link" title="My third tab">Tab3</t:actionlink>
</p>
<hr/>
</div>

<div id="secondary" class="yui-b">
Menu placeholder
</div>

<div id="yui-main">
	<div class="yui-b">
		<div id="mainContent">
			<t:body/>
			<t:delegate to="selectedTab"/>
			<t:block>
				<div t:id="tab1"/>
				<div t:id="tab2"/>
				<div t:id="tab3"/>
			</t:block>
		</div>
	</div>
</div>

<div id="ft">
<hr/>
<p align="center">Placeholder for footer</p>
</div>

</div>
</body>
</html>
}}}

Pay special attention to the {{{<t:actionlink t:id="tab1_link" ...}}}, which declares the tabs. Furthermore, the {{{<t:delegate to="selectedTab"/>}}} instruction above is a placeholder for your new component. Since a delegate doesn't do any rendering (i.e. no output is generated), it requests the Start.java file to return a component which can render itself. This component can have the name tab1, tab2 or tab3, and replaces the respective {{{<div t:id="tab1"/>}}} in the {{{<t:block>}}} statement. 

To complete the story, I'll show you the Start.java file:
{{{
package org.example.myapp.pages;

import org.apache.tapestry.annotations.Component;
import org.apache.tapestry.annotations.Persist;

import org.example.myapp.components.Tab;

public class Layout {
	// All components need to be declared (otherwise, your page will generate an error, since the <t:block> block
	// expects them and names them.
	@Component private Tab tab1;
	@Component private Tab tab2;
	@Component private Tab tab3;

	// Due to the page redirect, you need to persist the value briefly
	@Persist("flash")
	private int selectedComponent;

	// These are the event methods that are generated by clicking on the Tab. Due to the specific
	// syntax, onActionFromIDNAME, there is no need for the @OnEvent annotation, although that would work too.
	public void onActionFromTab1_link() {	selectedComponent = 1; }
	public void onActionFromTab2_link() {	selectedComponent = 2; }
	public void onActionFromTab3_link() {	selectedComponent = 3; }

	// Returns the selected tab, which will take the place of the corresponding <div t:id="tabXXX"/>
	public Object getSelectedTab() {
		switch (selectedComponent) {
		case 1:
			tab1.setMessage("tab1");
			return tab1;
		case 2:
			tab2.setMessage("tab2");
			return tab2;
		case 3:
			tab3.setMessage("tab3");
			return tab3;
		default:
			tab1.setMessage("tab1");
			return tab1;
		}
	}
}
}}}

That should do the trick.

To recapitulate, the flow of events is as follows:
 1. Initially, the Start page is requested to render. The {{{<t:delegate...}}} will request the tab object to display
 1. Start.java contains a method, getSelectedTab(), which is invoked, and by default returns the component called tab1. )Since all components are the same, I've set the message to tab1 as well to distinguish them).
 1. Since the component tab1 is returned, it takes the place in the {{{<t:block...}}} statement
 1. When the user clicks on tab2, Tab2_Link is invoked, which calls onActionFromTab2_link(). The only thing this does is to set the selectedComponent to 2. '''(Question: would it be possible to have an onActionFromTab(int selectedComponent) method instead?)'''
 1. The page is re-rendered, but since the selectedComponent is 'flashed' to persist, during the next rendering, its value is still 2
 1. We now return to the start, but tab2 is returned, and therefore the message will be "tab2".

---------------------------------------------------------------------
To unsubscribe, e-mail: dev-unsubscribe@tapestry.apache.org
For additional commands, e-mail: dev-help@tapestry.apache.org