Skip to main content

JSF Tutorial – Templates and Composite Components

Templates

Templates are reusable web page frames that allow us to insert contents to replace the placeholders and construct a complete web page. In the standard HTML web page development, we have to rewrite boilerplate tags on every page, but with templates, we can extract common parts, header and footer e.g., into a template, and specify which parts of the page can be changed, and delegate them to the using pages for implementation.

Here are the steps to create a template in JSF and how it can be used.

Project Source code: Templates and Composite Components

Create template.xhtml

Under the WebContent folder of the Notebook module, create a file named template.xhtml with the following code:


<?xml version="1.0" encoding="UTF-8"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:ui="http://xmlns.jcp.org/jsf/facelets" xmlns:f="http://xmlns.jcp.org/jsf/core"> <h:head> <title>Qiantu - A simple Notebook</title> <h:outputStylesheet library="css" name="style.css" /> </h:head> <h:body> <header id="header" class="header"> <p>This is header area</p> </header> <div id="content" class="content"> <ui:insert name="content">This will be replaced</ui:insert> </div> <div id="footer" class="footer"> <p>This is footer area</p> </div> </h:body> </html>

Template tags in JSF are included in the http://xmlns.jcp.org/jsf/facelets namespace. This template contains a header, a footer, and the content to be replaced. The <ui:insert> tag means that its content will be replaced by the actual definition in the using pages according to its name attribute. As a result, you will not see the content This will be replaced in the final output.

The <h:outputStylesheet> is the same as the <h:outputScript> tag described in the last blog: JSF Tutorial – Add Ajax Support to the Application

The using page

The page which uses a template is called a using page. We will update our index.xhtml under WebContent folder to use the template created above. Here is the code:


<!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:h="http://xmlns.jcp.org/jsf/html" xmlns:jsf="http://xmlns.jcp.org/jsf" xmlns:f="http://xmlns.jcp.org/jsf/core" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:nbcomp="http://java.sun.com/jsf/composite/components" xmlns:c="http://xmlns.jcp.org/jsp/jstl/core"> <h:body> <ui:composition template="template.xhtml"> <ui:define name="content"> <h1 class="title">Hello, welcome!</h1> <div class="menu"> <p><h:outputLink id="registerLink" onclick="toggleRegistrationForm()" value="#">Register</h:outputLink></p> <h:form> <p><h:commandLink id="listUsers" action="#{userBackBean.getAllUsers}" actionListener="#{userBackBean.increaseClickCounts}">List All Users</h:commandLink></p> </h:form> </div> <div class="add_note"> <p>Please add a note: </p> <h:form id="note_form"> <label for="username">Name: </label> <h:inputText id="username" value="#{noteBackBean.name}" /><br /> <label for="email">email: </label> <input type="email" jsf:id="email" value="#{noteBackBean.email}" /><br /> <label for="note">Note: </label> <h:inputTextarea id="note" rows="8" value="#{noteBackBean.note}"></h:inputTextarea><br /> <input type="submit" jsf:id="submit" jsf:action="response" value="Submit" /> </h:form> </div> <!-- Add category section --> <div class="add_category"> <h:form id="category_form"> <p> <h:outputLabel for="categoryName">Create a new category: </h:outputLabel> <h:inputText id="categoryName" value="#{noteBackBean.categoryName}" /> <h:commandButton action="#{noteBackBean.addCategory}" value="Add"> <h:outputScript target="head" library="js" name="script.js" /> <f:ajax render="categories" execute="categoryName" onevent="handleEvent" /> </h:commandButton> </p> </h:form> </div> <!-- Show all categories --> <div class="show_categories"> <ul jsf:id="categories"> <c:forEach items="${noteBackBean.categories}" var="category"> <li>${category}</li> </c:forEach> </ul> </div> <!-- Registration Form --> <nbcomp:registrationForm id="registrationForm" className="hide" userBackBean="#{userBackBean}" submitBtnText="Register" /> </ui:define> </ui:composition> </h:body> </html>

The template file is referenced by using <ui:composition> tag, and the template attribute denotes which file to use. The <ui:define> tag is used to define the actual content to replace the <ui:insert> tag in the template file based on the value of the name attribute. If you run the application, you will see the index page now looks like this:

Composite Components

Composite components are a series of components that form a functional component such as a registration form, a sidebar. They can be reused in every page through the tags representing them. Here are steps on how to create a composite component, using a registration form as an example.

Create the composite component file

Composite components are resources, which means you need to define them in the resource folder, WebContent/resources/components/ e.g., under this folder, create a file named registrationForm.xhtml with the following code:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://xmlns.jcp.org/jsf/html"
      xmlns:composite="http://java.sun.com/jsf/composite"
      xmlns:jsf="http://xmlns.jcp.org/jsf"
      xmlns:f="http://xmlns.jcp.org/jsf/core">

    <composite:interface>
        <composite:attribute name="userBackBean" required="true" />
        <composite:attribute name="className"/>
        <composite:attribute name="submitBtnText" default="Submit" />
        <composite:attribute name="formTitle" default="Registration Form"/>
    </composite:interface>

    <composite:implementation>
        <div id="#{cc.attrs.id}" class="#{cc.attrs.className}">
            <h1>#{cc.attrs.formTitle}</h1>
            <h:form>
                <h:panelGrid columns="2">
                    <label for="username">Username: </label>
                    <h:column>
                        <h:inputText id="username" value="#{cc.attrs.userBackBean.user.username}"
                                     valueChangeListener="#{cc.attrs.userBackBean.userNameChanged}" >
                            <f:validateRegex pattern="[a-zA-Z0-9_]{6,}"/>
                        </h:inputText>
                        <h:message for="username" />
                    </h:column>
                    <label for="password">Password: </label>
                    <h:inputSecret id="password" value="#{cc.attrs.userBackBean.user.password}" />
                    <label for="date_of_birth">Date of birth: </label>
                    <input type="date" jsf:id="date_of_birth" value="#{cc.attrs.userBackBean.user.dateOfBirth}">
                        <f:convertDateTime pattern="yyyy-MM-dd"/>
                    </input>
                    <label for="email">Email: </label>
                    <input type="email" jsf:id="email" value="#{cc.attrs.userBackBean.user.email}" />
                    <label for="phone">Phone number: </label>
                    <h:inputText id="phone"  value="#{cc.attrs.userBackBean.user.phone}" />
                    <h:commandButton value="#{cc.attrs.submitBtnText}" action="#{cc.attrs.userBackBean.register}" />
                </h:panelGrid>

            </h:form>
        </div>

    </composite:implementation>
</html>


JSF provides composite component tags under xmlns:composite="http://java.sun.com/jsf/composite" namespace. The <composite:interface> defines the contract of the component in which you need to specify which attributes will be used in this component using <composite:attribute> tag. You need to specify the name of the attribute, whether it is required, and the default value if not required. The type of the attributes can be String, numbers, methods signature, or managed beans, for example, the userBackBean will take a managed bean as its value.

The <composite:implementation> tag is where you will define your actual content of the composite component. Here is a simple registration form taken from the previous example. The cc.attrs is an implicit prefix for referencing the attribute value passed by the user or the default value. For example, the #{cc.attrs.userBackBean.user.username} will access the username property of the userBackBean managed bean.

To use this composite component, in the using page, the index.xhtml page showed in the templates section, add the namespace xmlns:nbcomp="http://java.sun.com/jsf/composite/components" of which the prefix can be customized. The last section of the URI is the name of the folder that the component file locates in. To use the registrationForm composite component, simply write one line of the code <nbcomp:registrationForm id="registrationForm" className="hide" userBackBean="#{userBackBean}" submitBtnText="Register" />. The final result is shown below: