Creating a Composite Component

Table of Contents

Creating a Composite Component Tutorial

Although Facelets and composite components were present in JSF 1.x, the release of JSF 2 has seen a strong focus on the Facelets view technology. As part of this focus composite components have been substantially improved with new features and bug fixes.

This tutorial will deal with creating a custom composite component for use in our demo application. This tutorial assumes the reader has an understanding of JSF and ICEfaces and creating and working with projects related to those technologies.

The goal of this tutorial is to create a basic ICEfaces project and simplify it with the addition of a "fieldSet" composite component, which will wrap the common grouping of an outputlabel, inputText, and message tag. The example itself is rather simple and allows a user to input their name.



Development Tools Used

The following tools were used to create the project.

1. Make the compositeComponent Project

  • Using Eclipse create a new Dynamic Web Project called compositeComponent.
    • Target runtime: Apache Tomcat v7.0
    • Dynamic web module version: 3.0
    • Configuration: JavaServer Faces v2.0 Project (Mojarra)

2. Add ICEfaces

Add the icefaces.jar to your project from the ICEfaces(2+) bundle. This can be added to the project through a custom User Library or by putting it into compositeComponent/WEB-INF/lib/. The approach doesn't matter as long as the jar is included in the deployed war file.

3. Create main.xhtml

Create a new page called main.xhtml and paste the code below:

main.xhtml
<!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:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core">
<h:head>
	<title>Composite Component - Main</title>
</h:head>
<h:body>
	<h:form>
		<h:panelGrid columns="3">
			<f:facet name="header">
				Hello, what is your name?
			</f:facet>

			<h:outputLabel for="firstName" value="First Name"/>
			<h:inputText id="firstName" value="#{nameBean.firstName}" required="true"/>
			<h:message for="firstName"/>

			<h:outputLabel for="lastName" value="Last Name"/>
			<h:inputText id="lastName" value="#{nameBean.lastName}" required="true"/>
			<h:message for="lastName"/>

			<f:facet name="footer">
				<h:commandButton value="Submit"/><br/>
			</f:facet>
		</h:panelGrid>

		<h:outputText value="Greetings '#{nameBean.firstName} #{nameBean.lastName}'."
		  		      rendered="#{nameBean.hasName}"/>
	</h:form>
</h:body>
</html>

This basic page prompts the user for their first and last name, and displays the result after the user has submitted the form. The page doesn't use any composite components, but the similar, repeated markup of h:outputLabel, h:inputText, and h:message is a perfect candidate for a composite component.

4. Create NameBean.java

Create a new Java class file called NameBean in the package org.icefaces.tutorial.composite.beans and paste the code below:

NameBean.java
package org.icefaces.tutorial.composite.beans;

import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean(name="nameBean")
@SessionScoped
public class NameBean {
	private String firstName;
	private String lastName;

	public String getFirstName() {
		return firstName;
	}
	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}
	public String getLastName() {
		return lastName;
	}
	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public boolean getHasName() {
		return firstName != null &&
		       lastName != null;
	}
}

As with the page this backing bean is very simple. We manage the first and last name variables, and also have a convenience method to determine if a name has been entered or not.

5. Deploy the Application

Build and deploy the application. When visiting the page it will appear similar to the following:

When entering a name and pressing the "Submit" button you will see "Greetings, firstName lastName" appear at the bottom.

6. Create a Composite Component

Now that we have a basic application running we'll improve and modularize the code with a JSF 2 composite component. Composite components are more convenient to create (compared to creating a new component from the ground up) and allow imaginative combinations of markup to be packaged into an easy-to-use form. The purpose of composite components can be to modularize common, reusable functionality, or to introduce new functionality through combining existing tags.

In our above example the firstName and lastName chunks of markup are similar, and instead of repeating the markup as we add more fields, we can package the markup into a composite component.

6a. Create Directory Structure

First we will set up the directory structure used by the composite component. This will affect the namespace used by parent pages. Make the following directory structure in your Eclipse project:

web/resources/components/example/

6b. Create Component Page

Inside the example/ directory created above, create a new page called fieldSet.xhtml and paste the code below:

fieldSet.xhtml
<?xml version="1.0" encoding="utf-8"?>
<html xmlns="http://www.w3.org/1999/xhtml"
      xmlns:h="http://java.sun.com/jsf/html"
      xmlns:f="http://java.sun.com/jsf/core"
      xmlns:composite="http://java.sun.com/jsf/composite">
	<composite:interface>
		<composite:attribute name="label" />
		<composite:attribute name="value" required="true" />
		<composite:attribute name="required" default="false" />
		<composite:editableValueHolder name="input" />
	</composite:interface>

	<composite:implementation>
		<h:outputLabel for="input" value="#{cc.attrs.label}" style="float:left;"/>
		<h:inputText id="input"
                             value="#{cc.attrs.value}"
                             style="float:left;"
                             required="#{cc.attrs.required}"/>
		<h:message for="input" id="msg"/>
	</composite:implementation>
</html>

This is our composite component interface and implementation. The interface declares how the composite component will be used in a parent page, including available attributes and the default values for those attributes. The implementation actually holds the markup that is inserted into the parent page when the composite component is used.

The new markup used in our page as a result of creating the composite component:

Example Usage
<ourComp:fieldSet label="Some Label Text" value="#{someBean.binding}"/>

7. Using a Composite Component

The above step is all we needed to do to create a composite component. Now let's use it!

7a. Add Namespace

First add the default composite component namespace generated by our directory structure, so that we can access the fieldSet.xhtml from our main.xhtml page.

The default namespace is http://java.sun.com/jsf/composite/, and the child directories of our resources/ directory specifies the remainder of the namespace. Because our directory is resources/components/example/ our final namespace is as follows:

main.xhtml header
...
xmlns:ourComp="http://java.sun.com/jsf/composite/components/example"
...

In the <html> container at the top of main.xhtml add the namespace above.

7b. Use fieldSet.xhtml

Now let's replace our first and last name markup with the new composite component. Replace the existing <h:panelGrid> with the following code:

New panelGrid
<h:panelGrid>
	<f:facet name="header">
		Hello, what is your name?
	</f:facet>

	<ourComp:fieldSet label="First Name" value="#{nameBean.firstName}" required="true"/>

	<ourComp:fieldSet label="Last Name" value="#{nameBean.lastName}" required="true"/>

	<f:facet name="footer">
		<h:commandButton value="Submit"/><br/>
	</f:facet>
</h:panelGrid>

The markup is simpler while still remaining functional. If we needed more attributes than the three (label, value, required) we made available in the composite component, we could just modify the composite:interface to add more "passthrough" attributes, such as maxlength, size, style, etc.

8. Add Middle Name

Now let's add a new field, just to see how simple the process is.

8a. Add to Bean

Add the following variable to NameBean.java and generated getters/setters for it:

Middle Name Variable
private String middleName;

* Also modify the getHasName method to include the new middleName variable.

8b. Add to Page

Add the new middleName by pasting another instance of our new composite component between the firstName and lastName markup in main.xhtml:

Middle Name Markup
    <ourComp:fieldSet label="Middle Name" value="#{nameBean.middleName}" required="true"/>

* Also modify the outputText at the bottom to include displaying the new #{nameBean.middleName} variable.

9. Re-Deploy the Application

Re-build and re-deploy the application and visit the page again. You'll see that the actual rendering of the page is very similar, but from a developer point of view the code is much cleaner to maintain and view. For example if we wanted to style the h:message we could add a single styleClass to the composite component, instead of having to copy-paste the change throughout our entire codebase (which in a large application may have hundreds of similar field setups as we've seen above).


Tutorial Source Code Downloads

Example Source Notes
Composite Component project compositeComponent source code Basic example project demonstrating how a composite component can be created and added to an ICEfaces 2 page.
Enter labels to add to this page:
Please wait 
Looking for a label? Just start typing.

© Copyright 2018 ICEsoft Technologies Canada Corp.