In JSF 2.x reusable components can be created using any one of the three approaches: Tag Files, Composite Components or Custom Components.
Templates and facelets are at the core of re-usability feature of JSF, so before we begin discussing custom components, here is a very brief intro about facelets and templates.Facelets
In JavaServer Faces (JSF) 2.x, Facelets is the default view declaration language. Facelets is a light weight templating framework. A typical facelet application may consist of multiple facelets, some of which could be re-usable facelets, for example header and footer; while others like the main business facelet could be different in each facelet application. What's important is that all facelets in a template view work as an entity.Template
A template file is basically an XHTML file which defines the overall structure or layout of a page. Different facelets application can refer to same template to render pages with a common layout.Tag files
Tag files is a way to define custom tag. This is generally used when there are multiple tags to be rendered but for brevity or to reduce code repetition, only one tag is used i.e. it is merely a way to combine two or more existing components/tags into one.Lets look at an example: This example uses Apache "Tomahawk" JSF component libraries "dataTable" component. This component has an inbuilt column 'sort' feature and one can provide custom images to show sort order (ASC or DESC).
In "dataTable" component, "column header" and "sorting" information is defined in a header facet. Now as mostly is the case, a dataTable usually has more than one column; so having a header facet with sort image information in every column declaration clutters the code (see highlighted code).
<t:dataTable id="contactList" var="contact" value="#{contactListViewBean.contacts}" sortable="true" sortColumn="#{contactListViewBean.sort}" sortAscending="#{contactListViewBean.ascending}"> <t:column sortable="true"> <f:facet name="header"> <t:commandSortHeader columnName="contactName" arrow="false" id="cNameId"> <h:outputText value="ContactName" /> <f:facet name="ascending"> <t:graphicImage value="uparrow.png" rendered="true" alt="Soem desc" /> </f:facet> <f:facet name="descending"> <t:graphicImage value="downarrow.png" rendered="true" alt="Soem desc" /> </f:facet> </t:commandSortHeader> </f:facet> <h:link id="contactNameId" value="#{contact.displayString}" outcome="ViewContact"> <f:param name="contactId" value="#{contact.primaryKey}" /> </h:link> </t:column> <t:column sortable="true"> <f:facet name="header"> <t:commandSortHeader columnName="contactName" arrow="false" id="cAddrId"> <h:outputText value="ContactAddress" /> <f:facet name="ascending"> <t:graphicImage value="uparrow.png" rendered="true"alt="Soem desc" /> </f:facet> <f:facet name="descending"> <t:graphicImage value="downarrow.png" rendered="true" alt="Soem desc" /> </f:facet> </t:commandSortHeader> </f:facet> <t:outputText id="contactAddressId" value="#{contact.contactAddress.displayString}" title="Address of the Contact" /> </t:column> </t:dataTable>
Now the goal is to reduce the code clutter because of code repetition using Tag file feature of JSF2.0.
Following is a list of to do or items that are required to implement custom tag using tag files.
Step No. | Description |
---|---|
1 | Create a source(xhtml) file and define contents in it using ui:composition tag. This file is put in web-inf/tags folder of the web application. |
2 | Create a tag library descriptor (.taglib.xml file) and declares the above custom tag in it. Make sure that the namespace and tag name is unique for the application. This file is put in WEB_INF folder of the web application. |
3 | Register the tag libray descriptor in web.xml |
4 | Use above declared tag in a XHTML file |
Putting the above steps in perspective of our example- put all the common code of header declaration in a tag file (step 1), and then define a custom tag for simplified access to the common code (step 2), register the new tag (step 3) and then use the new tag in a XHTML page (step 4)
Step 1. Create a source file/facelet containing all the tags/components required for column header.
DataTableColumn.xhtml
Create a folder "tags" in the webroot/WEB-INF folder of your project and save file as DataTableColumn.xhtml. If you notice the value of attributes "id" and "columnName" are EL variables - "column_Name_Attr" & "header_Name_Attr"; as you will see in step 4. these are nothing but attributes of the custom tag and you can pass in values to customize the column header information.
Step 2. Create a custom tag - this is done by creating a library desciptor file "column.taglib.xml" containing unique namespace, tagname and location of the source file.
column.taglib.xml.
<?xml version="1.0" encoding="UTF-8"?> <facelet-taglib xmlns="http://java.sun.com/xml/ns/javaee" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facelettaglibary_2_0.xsd" version="2.0"> <namespace>http://incept.com/column</namespace> <tag> <tag-name>tableColumn</tag-name> <source>tags/DataTableColumn.xhtml</source> </tag> </facelet-taglib>
Step 3. Register tag library in web.xml
web.xml
<context-param> <param-name>javax.faces.FACELETS_LIBRARIES</param-name> <param-value>/WEB-INF/column.taglib.xml</param-value> </context-param>
Step 4. Use newly defined/declared tag in a Facelet
At the top of the xhtml file, first define the namespace for tag
<html xmlns="http://www.w3.org/1999/xhtml" xmlns:f="http://java.sun.com/jsf/core" xmlns:h="http://java.sun.com/jsf/html" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:t="http://myfaces.apache.org/tomahawk" xmlns:lsColumn="http://incept.com/column" lang="en">
<t:dataTable id="contactList" styleClass="scrollerTable" var="contact" value="#{contactListViewBean.contacts}" sortable="true" sortColumn="#{contactListViewBean.sort}" sortAscending="#{contactListViewBean.ascending}" rows="50" title="Results for selection" rowCountVar="tabIndexCount" rowIndexVar="row"> <t:column sortable="true"> <lsColumn:tableColumn column_Name_Attr="contactName" header_Id_Attr="contact_name_Header" header_Name_Attr="Name" /> <h:link id="contactNameId" value="#{contact.displayString}" outcome="ViewContact"> <f:param name="contactId" value="#{contact.primaryKey}" /> </h:link> </t:column> <t:column sortable="true"> <lsColumn:tableColumn column_Name_Attr="contactAddress" header_Id_Attr="contact_address_Header" header_Name_Attr="Address" /> <t:outputText id="contactAddressId" value="#{contact.contactAddress.displayString}" title="Address of the Contact" /> </t:column> </t:dataTable>
Composite Component
This process requires less work as compared to previously discussed Tag Files. Basically we no longer need a taglib file and don't need to register it in web.xml either.Step No. | Description |
---|---|
1 | Create Composite Component: Create a source(xhtml) file and use composite:interace and composite:implementation ( where xmlns:composite="http://java.sun.com/jsf/composite") tags to define interface (attributes, methods) and implementation (Using other JSF tags to render etc.). |
2 | Save the source file in resources/<subfolder> folder of the web application. |
3 | Use above defined tag/composite component in a facelet. |
Here is a very simple source file.
Step 1. Create a source file: LabelAndField.xhtml
Things to note are
1) Namespace declaration at the top of the file
xmlns:composite="http://java.sun.com/jsf/composite"
2) Interface declaration within <composite:interface>: Contents within this tags define the usage contract for a composite component. This section defines configurable items (attributes, actions etc) of the composite tag. This tag also has an optional attribute "name", which if declared becomes the name of the tag else by default the tagName is same as file name.
3) Implementation declaration within <composite:implementation>; this section contains the actual work or the XHTML markup of the composite tag. The attributes and methods declared within the <composite:interface>block can be accessed within <composite:implementation> by using EL syntax #{cc.attrs.atribute-Name}.
The word "cc" is reserved in JSF for composite components and can give you things like the parent component, children in a list, ID etc., "attrs" refers to attributes, cc.attrs gives you a map of attributes defined in the "interface" section and you can access a specific attribute using the "attribute-Name".
Step 2. Save the file in web-inf/resources/<subfolder>
In the above example the file is stored in resources/ezcomp. Step 3 points out significance of folder/path..
Step 3. Use composite tag in a facelet. Create a facelet, say - compositedemo.xhtml
Things to note are
1) Namespace declaration at the top of the file
xmlns:ezcomp="http://java.sun.com/jsf/composite/ezcomp"
The first part defines that the namespace for the composite component is "ezcomp", and the second part declares where in the resources folder to find the definition of this composite component. In this example its in the "ezcomp" subfolder inside resources.
2) This is how you use the tag: namespace:FileName; inour example ezcomp:LabelAndField, LabelAndField is the name of the file, so by default its the tag name;
<ezcomp:LabelAndField opLabelId="fNLabel" opLabelName="First Name"
fieldId="fnInput" bindTo="#{person.firstName}"/>
Run/ deploy file and you should see
Custom Components
Custom components are required if the existing components provided by the default implementation (mojarra or myfaces) or the extended components (primefaces etc) do not provide the desired functionality.
In cases, where in you need to make addition/change to markup generated by JSF components, then you can use a custom renderer.
Here is an example of how to add ARIA specific attribute to standard components. (By default, if you include an unspecified attribute in an jsf component, that attribute is dropped ie. not rendered in the resultant html)
Following is a list of to do or items that are required to implement custom renderer.
Step No. | Description |
---|---|
1 | Create a custom renderer extending the renderer type. |
2 | Register the renderer in faces-config.xml. |
3 | Use standard tag in a XHTML file |
Step. 1. Create Custom Renderer
This example extends standard "TextRenderer" to render ARIA attributes in the rendered html.
public class MyInputTextRenderer extends TextRenderer { @Override protected void getEndTextToRender(FacesContext context, UIComponent component, String currentValue) throws java.io.IOException { String[] attributes = {"aria-required", "aria-readonly", "aria-autocomplete","aria-multiline"}; ResponseWriter writer = context.getResponseWriter(); for (String attribute : attributes) { String value = (String) component.getAttributes().get(attribute); if (value != null) { writer.writeAttribute(attribute, value, attribute); } } super.getEndTextToRender(context, component, currentValue); } }
Step. 2. Register renderer in faces-config.xml
<render-kit> <renderer> <component-family>javax.faces.Input</component-family> <renderer-type>javax.faces.Text</renderer-type> <renderer-class>com.incept.MyInputTextRenderer</renderer-class> </renderer> </render-kit>
Step. 3. Use standard tag in XHTML file
<h:outputLabel id="labelFortxt1" for="txt1" value="Aria Field"/> <h:inputText id="txt1" value="Hello" aria-required="true" aria-readonly="true" aria-Not_A_Declared_Attr="ShouldNotDisplay"/>
The resultant/rendered html will contain "aria-required" and "aria-readonly" attributes. Attribute "aria-Not_A_Declared_Attr" is not displayed because its not in the "attributes" array defined in the renderer.
Prasanna Bhale
Very clear.....Thanks.
ReplyDeleteGreat!!
ReplyDeleteJava books
Thank you.
ReplyDeletefor learning trending software courses online please log on to www.techenoid.com
ReplyDeleteNice Information,Thank You Very Much For Sharing.
ReplyDeleteSalesforce CPQ Online Training Hyderabad
Salesforce CPQ Online Training India
Excellent blog I visit this blog. It's really awesome.
ReplyDeleteWorkday Online Training in India
Workday Online Training in Hyderabad