Facelets is implemented as a JSF ViewHandler
, which can be easily configured in your application. Simply make sure the Facelets JAR (jsf-facelets.jar
) is in your project's classpath and add a single modification to your faces-config.xml
:
JavaServer Faces defaults to JSP files for defining views (*.jsp
). You will want to change this to some other type in your WEB-INF/web.xml
.
According to the context-param
defined above, you can start writing Facelets and saving them with .xhtml
extensions. You are now ready to start using Facelets.
Facelets comes ready to use all of the UIComponents
in the JavaServer Faces API under the same rules as you would with JSP. This means you may reference the online documentation for JSF's JSP tag libraries.
There is also a simple version of JSTL's core library available which gives you
<c:foreach></c:foreach>
and
<c:if></c:if>
.
Finally, there is a UI tag library built into Facelets which gives you all of the templating and re-use capabilities. More on this in another article.
Facelets only needs to be proper XML. They aren't dependent on XHTML syntax and could be used along side WML, SVG, or even SOAP. With JSP, you often times import tag libraries, but Facelets just looks for namespaces at compilation, similar to JSPX. Here is the start of a XHTML Facelet document:
You should be able to save this code as test.xhtml
and then immediately request test.jsf?name=Jacob
(if your FacesServlet
was mapped to *.jsf
). This should be a good test to guarantee that you are setup correctly for Facelets.
Facelets uses the new EL-API specification with JSF. This means that you can use either ${ }
or #{ }
interchangeably unlike JSP. As the example above shows, expressions can be inlined with regular text.
If you are using JSF 1.2, you can add your own custom ELResolvers
for integrating Spring, EJB, JNDI, etc.-- all at once if you choose. You can even have ELResolvers
of your own that work with objects specific to your application.
The EL-API is part of the JSP 2.1 specification, but doesn't have any dependencies on JSP or JSF. This means that it can be used with Facelets in any kind of deployment.
In the example test.xhtml
above, you will notice that there are extra namespaces declared. These are the same namespaces included in the JavaServer Faces API for the built-in JSP tag libraries. Again, Facelets wants to build off of familiar territory and documentation.
When you start using the built-in JSF components with Facelets, it's a good idea to have the tag library documentation available.
Since we already have our test.xhtml
open, lets create a very simple form. Add the following code to the body of your page:
<h:form>
<h:commandbutton action="#{person.action}"></h:commandbutton>
- <h:form>
- <h:inputText value="#{person.name}"/>
- <h:commandButton action="#{person.action}"/>
- </h:form>
</h:form>
The next step would be creating your person bean and modifying your faces-config.xml
to back this simple example.
The previous example makes use of special tags that probably won't look so hot inside of an HTML designer tool like Dreamweaver. Facelets offers a different way to specify components within your page with the jsfc
attribute within a standard HTML element (much like Tapestry and Struts Shale's Clay plugin. )
The Facelets compiler looks for the jsfc
attribute for every element in the document. The value of the jsfc
attribute is an alternate element name or "qname" to replace the one the designer used in the page.
Facelets, at compile time, will instead create an h:inputText
component in the document while automatically wiring all of the available attributes.
Aliasing components allows the desinger's tool to see a normal HTML input
tag, but the programmer can treat it as a JSF component as specified by the jsfc
attribute.
As mentioned in the previous article, Facelets compiles valid XML. These files could be one or three lines long, again the only point is that they are valid XML.
Facelets uses SAX to compile the document into a stateless tree of TagHandlers
contained within a Facelet
object. Facelets are thread-safe and can easily be used in large scale deployments by multiple requests at once. Facelets will provide warning messages at compilation time along with information on libraries used and view handling within JSF.
Executing Facelets compilation is simple, as shown below:
java 代码
- // grab our FaceletFactory and create a Facelet
- FaceletFactory factory = FaceletFactory.getInstance();
- Facelet f = factory.getFacelet(viewToRender.getViewId());
-
- // populate UIViewRoot
- f.apply(context, viewToRender);
More information about Facelet's architecture will be provided in a later article.
Great care was taken with Facelets and it's error handling. Lets say for a moment that you mistyped an expression in an attribute. Facelets will generate an error like this at compilation:
- greeting.xhtml @18,97 <input action="#{success[}"> Error Parsing: #{success[}
As you can see above, Facelets provides you with error message that tell you what file, line, and column number the error occured. More specifically it also provides the element and attribute that was errant.
Facelets also uses the java.util.logging
package to handle output of debug messages. You can read more here on setting up logging with your JRE.
Facelets allows you to use an XML file or Java code to define your component libraries, although I will only go through the XML configuration option in this article as it is the preferred choice.
<facelet-taglib></facelet-taglib>
<component>
- <facelet-taglib>
- <namespace>http://www.mycompany.com/jsf</namespace>
- <tag>
- <tag-name>bar</tag-name>
- <component>
- <component-type>javax.faces.Data</component-type>
- <renderer-type>com.mycompany.Bar</renderer-type>
- </component>
- </tag>
- </facelet-taglib>
</component>
That's all you need to integrate your component into Facelets. These XML files can be refrenced in two ways:
web.xml
under the init-param "facelets.LIBRARIES
". These files are relative to your application, just like referencing Struts configuration files. META-INF
folder with a file extension of ".taglib.xml
". Facelets will automatically pick these up for compilation just as with JSP tld files. Within a facelet-taglib
file, you can also specify converters, validators, and custom TagHandlers
for ultimate control over document processing.
Validators and Converters can also be represented in a Facelet via the same facelet-taglib
file.
- <facelet-taglib>
- <namespace>http://www.jsfcentral.com/public</namespace>
- <tag>
- <tag-name>validateRegExp</tag-name>
- <validator>
- <validator-id>foo.bar.RegExp</validator-id>
- </validator>
- </tag>
- <tag>
- <tag-name>convertUtilDate</tag-name>
- <converter>
- <converter-id>foo.bar.UtilDate</converter-id>
- </converter>
- </tag>
- </facelet-taglib>
Again, Facelets works closely with JavaServer Faces's existing API and this is all you have to define. Facelets will automatically wire all properties on your Converter
or Validator
objects.
Okay, I haven't thought of it yet and you want to add your own custom tag to Facelets. This includes cases where you want unique behavior for wiring properties and attributes to UIComponents
, Validators
, or Converters
.
Facelets provides many foundation classes you can extend in order to make your job simpler. Let's first take a look at source for <c:if></c:if>
to give you an idea:
That's it! You will notice that Facelets uses the "good citizen" principle and constructor injection when the document is compiled. Facelets and all tags are stateless, unlike JSP (which also pools tag handler objects). More technical details are provided in a separate article, so lets describe what's going on in the tag.
TagHandler
, which <c:if></c:if>
extends, is a great example of the foundation classes you can use to develop custom tags. Think of TagHandler
as JSP's TagSupport
or BodyTagSupport
.
At construction, the representation of the Tag is passed (TagConfig
) which conveys the structure of the document including: location, child tags, and attributes.
TagAttributes
are representations of the attributes specified in the XML document. They have many methods for handling EL expressions and coercions to the types you need. They too are stateless and their methods require passing the FaceletContext
to provide the current state.
The last point of interest is that the children are represented by the member variable nextHandler
. You can apply the children as many times as you want or not at all as in the case of <c:if></c:if>
.
Once your custom tag is written, you can go ahead and add it to a facelet-taglib
file like so:
<facelet-taglib></facelet-taglib>
- <facelet-taglib>
- <namespace>http://www.jsfcentral.com/public</namespace>
- <tag>
- <tag-name>if</tag-name>
- <handler-class>com.jsfcentral.IfHandler</handler-class>
- </tag>
- </facelet-taglib>
This was just a short introduction to using the Facelets framework. More articles will follow that will go into greater detail with templating, features, and customizing Facelets.