In the previous recipe, you have seen the basic notions of developing a composite component in JSF 2.0. Our component just displayed a text suggesting that we are talking about a login operation, but that was all. Well, in this recipe, we will extend this composite component to make it look like a real login component.
We have developed this recipe with NetBeans 6.8, JSF 2.0, and GlassFish v3. The JSF 2.0 classes were obtained from the NetBeans JSF 2.0 bundled library.
First we modify the component.xhtml
page itself, as shown next:
<?xml version='1.0' encoding='UTF-8' ?> <!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" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:e="http://java.sun.com/jsf/composite/login"> <h:head> <title>Creating a login composite component</title> </h:head> <h:body> <h:form> <div id="compositeComponent" style="border: 2px solid #000;"> <e:login> <f:actionListener for="loginID" type="listeners.LOGINActionListener" /> </e:login> </div> <h:commandButton value="Reload" /> <h:outputText value="#{LOGINActionMessage}" /> </h:form> </h:body> </html>
This time we have attached an action listener that will deal with the provided user and password credentials (it intercepts the login events), and will populate the response with a message displayed by an outputText
component (a value is stored in request scope and then displayed).
This action listener is shown next:
package listeners; import javax.faces.component.UIComponent; import javax.faces.component.ValueHolder; import javax.faces.context.FacesContext; import javax.faces.event.AbortProcessingException; import javax.faces.event.ActionEvent; import javax.faces.event.ActionListener; public class LOGINActionListener implements ActionListener { public void processAction(ActionEvent evt) throws AbortProcessingException { FacesContext ctx = FacesContext.getCurrentInstance(); UIComponent ui_comp = evt.getComponent(); ValueHolder user, pwd; user = (ValueHolder) ui_comp.findComponent("usernameID"); pwd = (ValueHolder) ui_comp.findComponent("passwordID"); String msg = "Login fired!" + " User: " + user.getValue() + " Password: " + pwd.getValue(); ctx.getExternalContext().getRequestMap(). put("LOGINActionMessage", msg); } }
Next, let's focus on the component page, login.xhtml
. In the <composite:implementation>
tag, we have provided the components that form a login page with two text fields and a button (these are the sub-components of the composite component):
<!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" xmlns:ui="http://java.sun.com/jsf/facelets" xmlns:composite="http://java.sun.com/jsf/composite"> <h:head> <title>Creating a login composite component - not rendered on output </title> </h:head> <h:body> <composite:interface> <composite:actionSource name="loginID" /> <composite:valueHolder name="username" /> <composite:valueHolder name="password" /> </composite:interface> <composite:implementation> login composite componentcreating, in JSF 2.0<table> <tr> <td>Username:<h:inputText id="usernameID" /> </td> </tr> <tr> <td>Password:<h:inputSecret id="passwordID" /></td> </tr> <tr> <td><h:commandButton value="Login" id="loginID" /></td> </tr> </table> </composite:implementation> </h:body> </html>
The interesting part here is the <composite:interface>
tag body. Here, we indicate that the composite component has two inner components that implement javax.faces.component.ValueHolder
(any attached objects valid for ValueHolder
may be attached to the composite component). The values of the name
attribute must be the same as the id
values in the <composite:implementation>
tag.
The component is rendered as a composite component made of two text fields and one submit button. The button event (login action) is captured by the action listener, which is responsible for extracting the submitted user and password using the findComponent
method and preparing a response that is put in an outputText
component. The key of this mechanism is the fact that in JSF 2.0 every composite component implements javax.faces.NamingContainer
. By is nature, the findComponent
method searches for any child components that match the argument, then searches the ancestor component that implements NamingContainer
and asks it to find the component. In our case, an ActionListener
is passed an ActionEvent
whose source property is the component that fired the event, which means that component will be a child of our composite component.