The standard JSF HTML components that we have covered so far represent the basic building blocks of a JSF user interface. In GUI development terminology, the basic set of controls supported by HTML represents what can be described as base-level components, in the sense that buttons, checkboxes, and radio buttons cannot have other visible components nested inside them.
The organization of elements on the form is an essential step in the development of a JSF user interface. Determining the optimal arrangement, alignment, and distribution of user interface controls is an important decision-making process for UI designers that can have an impact on the usability of our application. Do we place labels to the left of controls or above them? Do we group components of the same type together on the screen, or do we group components together on the basis of their relationship to the data they are collecting?
One of the more powerful components in the JSF HTML component library is the HtmlPanelGrid
component. This component is rendered by the<h:panelGrid>
tag as an HTML<table>
element. Java GUI programmers will recognize similarities between the HtmlPanelGrid
component and the GridLayout
layout manager class from the Java Swing/AWT toolkit. As the<h:panelGrid>
tag renders an HTML table, let's start by taking a look at how to use a table-based approach to organize the components in our user interface.
The<h:panelGrid>
tag has a columns
attribute that specifies how many columns the component should render while it is laying out components on the screen. Understanding how to use this component is simple. Any child components that are nested within this component are arranged from left to right, then from top to bottom. The HtmlPanelGrid
component iterates its children, rendering each one as a table cell. Once the desired number of columns is reached, the component begins a new table row and repeats the process until all child components have been rendered. The result is a grid of components rendered on the screen as an HTML layout table.
Let's consider an example that uses all the components we have seen so far, to demonstrate how to lay out components on the screen using the JSF<h:panelGrid>
tag. Suppose we wanted to gather information from the user as part of a customer registration form. We need to collect the following information: the customer' s first and last names, date of birth, gender, phone number, e-mail address, country of origin, and relevant interests.
In this example, we use the<h:panelGrid>
tag to render a complex layout table that organizes other components into an attractive grid of controls on the screen.
<h:panelGrid columns="3"> <h:outputLabel for="firstName" value="#{bundle.firstNameLabel}" /> <h:inputText id="firstName" value="#{customerBean.customer. firstName}" required="true" /> <h:message for="firstName" errorClass="error" showSummary="true" showDetail="false" /> <h:outputLabel value="#{bundle.lastNameLabel}" for="lastName" /> <h:inputText id="lastName" value="#{customerBean.customer.lastName}" required="true" /> <h:message for="lastName" errorClass="error" showSummary="true" showDetail="false" /> <h:outputLabel for="dateOfBirth" value="#{bundle.dateOfBirthLabel}" /> <h:inputText id="dateOfBirth" value="#{customerBean.customer. birthDate}" required="true"> <f:convertDateTime pattern="M/d/yyyy" /> <f:validator validatorId="customDateValidator" /> </h:inputText> <h:message for="dateOfBirth" errorClass="error" showSummary="true" showDetail="false" /> <h:outputLabel for="gender" value="#{bundle.genderLabel}" /> <h:selectOneRadio id="gender" value="#{customerBean.customer.male}" required="true"> <f:selectItem itemLabel="Male" itemValue="true" /> <f:selectItem itemLabel="Female" itemValue="false" /> </h:selectOneRadio> <h:message for="gender" errorClass="error" showSummary="true" showDetail="false" /> <h:outputLabel for="phoneNumber" value="#{bundle.phoneNumberLabel}" /> <h:inputText id="phoneNumber" value="#{customerBean.customer. phoneNumber}" /> <h:message for="phoneNumber" errorClass="error" showSummary="true" showDetail="false" /> <h:outputLabel for="emailAddress" value="#{bundle. emailAddressLabel}" /> <h:inputText id="emailAddress" value="#{customerBean.customer. emailAddress}" required="true" /> <h:message for="emailAddress" errorClass="error" showSummary="true" showDetail="false" /> <h:outputLabel value="#{bundle.countryLabel}" for="country" required="true" /> <h:selectOneMenu id="country" value="#{customerBean.customer. countryOfOrigin}" required="true"> <f:selectItem itemLabel="Select" itemValue="" /> <f:selectItems value="#{customerBean.countrySelectItems}" /> <f:converter converterId="countryConverter" /> </h:selectOneMenu> <h:message for="country" errorClass="error" showSummary="true" showDetail="false" /> <h:outputLabel for="interests" value="#{bundle.interestsLabel}" /> <h:selectManyCheckbox id="interests" value="#{customerBean. interests}" layout="pageDirection"> <f:selectItem itemLabel="Java" itemValue="Java" /> <f:selectItem itemLabel="Architecture" itemValue="Architecture" /> <f:selectItem itemLabel="Web Design" itemValue="Web Design" /> <f:selectItem itemLabel="GUI Development" itemValue="GUI Development" /> <f:selectItem itemLabel="Database" itemValue="Database" /> </h:selectManyCheckbox> <h:message for="interests" errorClass="error" showSummary="true" showDetail="false" /> </h:panelGrid>
Sometimes, we may want to display more than one component in a particular table cell within the grid. For example, a common scenario is to render two or more buttons in a single column. We know the behavior of the HtmlPanelGrid
component is to render each child component in a separate cell, so how do we render more than one component per column?
The standard JSF HTML component library includes the HtmlPanelGroup
component for just this purpose. This component is rendered by the<h:panelGroup>
tag and allows us to group two or more components together. By grouping components together, we can then treat them as a single component. Therefore, if we nest two<h:commandButton>
tags within an<h:panelGroup>
tag, we can then place the<h:panelGroup>
inside the<h:panelGrid>
tag and our two controls will be rendered within a single table cell.
This example shows how to use the<h:panelGroup>
tag to group two buttons together in the same column in an<h:panelGrid>
tag.
<h:panelGrid columns="3"> <h:outputLabel for="firstName" value="#{bundle.firstNameLabel}" /> <h:inputText id="firstName" value="#{customerBean.customer.firstName}" required="true" /> <h:message for="firstName" errorClass="error" showSummary="true" showDetail="false" /> <h:panelGroup> <h:commandButton value="Save" actionListener="#{customerBean.saveCustomer}" style="margin-right:5px" /> <h:commandButton type="reset" value="Reset" /> </h:panelGroup> </h:panelGrid>