My blog has moved!

You should automatically be redirected in 6 seconds. If not, visit
http://blogs.i2m.dk/allan
and update your bookmarks.

Tuesday, 18 March 2008

Example of using custom facelet tags

Rather than using includes when creating templates you can create your own custom facelet tags without the husle of creating a JSF component from scratch. As an example, I'll create a menu tag that can be used to switch a selected menu item on and off.

Here is the menu (save it as /WEB-INF/facelets/tags/menu.xhtml):


<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
<h:panelGrid columns="10">
<h:outputText styleClass="menuItem" value="Home" />
<h:outputText styleClass="menuItem" value="Some page" />
<h:outputText styleClass="menuItem" value="Another page" />
<h:outputText styleClass="menuItem" value="Third page" />
</h:panelGrid>
</ui:composition>


Here is the cascanding style sheet:

.menuItem {
border: 1px solid black;
}

.menuItemSelected {
background: red;
color: white;
font-weight: bold;
}


To make the menu into a tag you create a facelets tag library. I'll call it mytags.taglib.xml and place it in /WEB-INF/facelets/:


<?xml version="1.0"?>
<!DOCTYPE facelet-taglib PUBLIC "-//Sun Microsystems, Inc.//DTD Facelet Taglib 1.0//EN" "http://java.sun.com/dtd/facelet-taglib_1_0.dtd">
<facelet-taglib>
<namespace>http://bigallan.blogspot.com/mytags
<tag>
<tag-name>menu</tag-name>
<source>tags/menu.xhtml</source>
</tag>
</facelet-taglib>


To make the web application recognise your tag library you need to update add the following context parameter to web.xml:

<context-param>
<param-name>facelets.LIBRARIES</param-name>
<param-value>/WEB-INF/facelets/mytags.taglib.xml</param-value>
</context-param>


If you have more than one custom tag library you can separate them with semicolons (;)

On your pages you can now include the menu tag:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:mytags="http://bigallan.blogspot.com/mytags">
<head>
<title>My page</title>
<link href="css/styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<mytags:menu home="true" />
</body>
</html>


All we need to do now is to look out for the attributes of the custom tag. Attributes are automatically turned into variables on the tag pages, so we simply make the following changes to /WEB-INF/facelets/tags/menu.xhtml:


<ui:composition xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core">
<h:panelGrid columns="10">
<h:outputText styleClass="menuItem #{home == 'true' ? 'menuItemSelected' : ''}" value="Home" />
<h:outputText styleClass="menuItem #{somePage == 'true' ? 'menuItemSelected' : ''}" value="Some page" />
<h:outputText styleClass="menuItem #{anotherPage == 'true' ? 'menuItemSelected' : ''}" value="Another page" />
<h:outputText styleClass="menuItem #{thirdPage == 'true' ? 'menuItemSelected' : ''}" value="Third page" />
</h:panelGrid>
</ui:composition>


To turn all menu items on you code do the following:


<?xml version='1.0' encoding='UTF-8' ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:h="http://java.sun.com/jsf/html" xmlns:f="http://java.sun.com/jsf/core" xmlns:mytags="http://bigallan.blogspot.com/mytags">
<head>
<title>My page</title>
<link href="css/styles.css" rel="stylesheet" type="text/css" />
</head>
<body>
<mytags:menu home="true" somePage="true" anotherPage="true" thirdPage="true" />
</body>
</html>


Enjoy!

Suggested reading on the subject:

11 comments:

Anonymous said...

Hi,

Nice blog.

Thanks,
Guddu
http://freesourceutilityhelp.googlepages.com

goodusername said...

Hi,

Great example!

I was wondering if there is any way to document the tag's attributes?
So far I've read that there is no possibility to scan the facelet, so I'm expecting some other solution.

Allan Lykke Christensen said...

You are right that there is no way of scanning a faclets file to determine the tag attributes, as any variable in the file could be a tag.

Instead you could document the tag by creating a TLD (Tag Library Descriptor) for the facelet file and then run it through a utility like taglibdoc which creates JavaDoc-like documentation for tag libraries.

Anonymous said...

Hi,

I tried ur example but my page displayed blank . The source code of the xhtml showed the tag as it was written , it was not replaced.
It means the tag was not recognised. Any inputs ?

Allan Lykke Christensen said...

Sounds like you missed the step of loading your tags into Facelets using the context parameter facelets.LIBRARIES.

Anonymous said...

Hi Allan,

Thanks for the prompt reply . But I have done that too. I have the entry in web.xml

Any other specific thing like should all the xxx.taglib.xml files be inside facelets folder only ?

Allan Lykke Christensen said...

You can place the taglib.xml files in any directory of the web application. If you have more than one Facelets taglib you should separate them using semicolons (;) and not commas (,).

Also check that the namespace is identical in both the taglib.xml and the XML namespace declaration on the XHTML page.

Anonymous said...

Hi Allan,

It worked , I copy pasted into some running code and it worked fine.

I have on more problem though not related with your issue .

I have an existing project which uses Myfaces and JSP . I have a custom Pagination tag in the project which works fine.
The tag is extending the org.apache.myfaces.taglib.core.ViewTag and has doStart and doEnd methods. For this tag there is a tld and which I have included in the jsp :

%@ taglib uri="/WEB-INF/PaginationTag.tld" prefix="p" %

This is my tag class :

public class PaginationTag extends ViewTag {
public int doStartTag(){
......
}
}

I havent made any other entry in faces-config or made any renderer/component for this tag.

I am trying to create the same tag in SUN JSF RI 1.2 and Facelets . But the tag doesn't seem to work . there is not error thrown but the xhtml file doesnot seem to understand the tag and it displays the tag as it is.

I have extended the com.sun.faces.taglib.jsf_core.ViewTag to create this tag since I am using SUN JSF RI 1.2 .

Do I need to create a Component class and/or a Renderer too . Why it was not required in myfaces case ?

How can I have the same functionality as I had in the JSP case like implement doStartTag() and other methods ?

Please help me some links or example codes you have ?

Also which books one should refer for Facelets and JSF ?

Allan Lykke Christensen said...

For custom tags you will need to provide Facelets with a taglib.xml like the one you created for this example.

An example of such a taglib.xml can be found on the MyFaces wiki where they are explaining how to get the Tomahawk JSF library to work with Facelets.

I can highlighly recommend The Facelets Essentials book from Apress written by my good friend Zubin Wadia.

Anonymous said...

Hi,
When i am giving home="true" only then other menus are displaying but without css. Is it possible that they should not be visible if the are not true?

Thanks for Reply.

Allan Lykke Christensen said...

Hi there,

home="true" should display the Home link with red background and white text, and the other links should be ordinary links. Ensure that you don't have any break lines in the lines of h:outputText. On the blog entry the lines may appear to be chopped into separate lines when each h:outputText is suppose to have only a single line.

P.S. The blog is being moved to http://blogs.i2m.dk/allan.