Skip to main content

JSF 2.0: Wiring up buttons in a component

Posted by driscoll on December 14, 2008 at 1:14 PM PST


In my previous two technical posts, I described a "switchlist", then added Ajax behavior using the f:ajax tag. If you haven't already, please go back and check on those posts to catch up to where we are.


Today, we'll take that basic Switchlist (not the Ajaxified one), and turn it into a component that you can drop into your page with a single tag. We'll Ajaxify the Switchlist compoenent once we've got it stuffed into a component. Now, we've already covered writing a simple composite component, as well as writing a number of more complex examples. We won't cover that material again, so you may want to review all of that before we start.


Done? Good. Let's get started.


First, as usual, let's look at what the tag will look like when we're done:



<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:ez="http://java.sun.com/jsf/composite/switchlist">
<h:head>
    <title>Switchlist Example</title>
</h:head>
<h:body>
        <h1>Switchlist Example</h1>
        <h:form id="form1">
             <ez:switchlist id="switchlist" selected1="#{listholder.list1}"
                                            selected2="#{listholder.list2}"
                                            items1="#{listholder.items1}"
                                            items2="#{listholder.items2}"
                                            move1to2="#{listholder.move1to2}"
                                            move2to1="#{listholder.move2to1}"/>
            <br/>
            <h:commandButton value="reload" type="submit"/>
            <h:messages/>
        </h:form>
</h:body>
</html>


Well, that's a lot of parameters, but there isn't really any way around that - the tag needs to juggle two lists (which needs 4 parameters), and two buttons (which needs two parameters). The parameters will still point to the same bean properties that we saw in the first post on switchlist.


As in other composite components, we say xmlns:ez="http://java.sun.com/jsf/composite/switchlist">, which is really saying "look in the resources directory, and find the directory switchlist". Then since the tag is "switchlist", it's a reference to resources/switchlist/switchlist.xhtml. Here's the full contents of that file, we'll go through the relevant parts line by line:



<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">
<head>
    <title>This will not be present in rendered output</title>
</head>
<body>
<composite:interface name="switchlist"
                     displayName="Switchlist Component"
                     shortDescription="A basic example of the composite component feature">

    <composite:attribute name="selected1" required="true"/>
    <composite:attribute name="selected2" required="true"/>
    <composite:attribute name="items1" required="true"/>
    <composite:attribute name="items2" required="true"/>
    <composite:attribute name="move1to2" targets="move1to2" required="true" method-signature="void f1(javax.faces.event.ActionEvent)" />
    <composite:attribute name="move2to1" targets="move2to1" required="true" method-signature="void f2(javax.faces.event.ActionEvent)" />

</composite:interface>

<composite:implementation>
    <h:outputStylesheet name="switchlist/switchlist.css"/>
    <h:selectManyListbox value="#{compositeComponent.attrs.selected1}" styleClass="switchlist">
        <f:selectItems value="#{cc.attrs.items1}"/>
    </h:selectManyListbox>
    <h:panelGroup id="buttonGroup" styleClass="switchlistButtons">   
    <h:commandButton id="move1to2" value="&gt;&gt;" actionListener="#{cc.attrs.move1to2}" styleClass="switchlistButton"/>
    <h:commandButton id="move2to1" value="&lt;&lt;" actionListener="#{cc.attrs.move2to1}" styleClass="switchlistButton"/>
    </h:panelGroup>
    <h:selectManyListbox value="#{cc.attrs.selected2}" styleClass="switchlist">
        <f:selectItems value="#{cc.attrs.items2}"/>
    </h:selectManyListbox>
</composite:implementation>
</body>
</html>


Since the new bits are the most important, I'll cover wiring the buttons up. That happens in two places. The first is in defining the interface, when we say:


composite:attribute name="move1to2" targets="move1to2" required="true" method-signature="void f1(javax.faces.event.ActionEvent)"


We define the name of the attribute (move1to2), the target of the attribute (move1to2), which in our example is named the same, but doesn't have to be - it's the id of the button that has the actionListener.


Then we define the signature of the method that that named id will call, in this case, void f1(javax.faces.event.ActionEvent) - which is just the method signature for any function that's called by an actionListener event. This is the markup that informs the composite component handler that there's a method value, rather than a value expression, contained in the #{cc.attrs.move1to2} expression that we put on that button.


A little wordy, but easy enough to understand once you've seen it done.


The rest of the example just uses the same kinds of markup that we've already seen done on other composite components.


In my next post, I'll cover wiring this up to use Ajax - and I'll be going back to using the jsf.ajax JavaScript API, since I'd like to add a few extra features above what we've already done.


As always, if you've got questions on how this all works, please ask below.

Related Topics >>

Comments

Thanks, Ed. I'm going to go ahead an modify that in the demo. For everyone else: As always, you can find these demos that I discuss in the Project Mojarra codebase, under the jsf-demo directory. (That's http://mojarra.dev.java.net)

JD> <ez:switchlist id="switchlist" selected1="#{listholder.list1}" JD> selected2="#{listholder.list2}" JD> items1="#{listholder.items1}" JD> items2="#{listholder.items2}" JD> move1to2="#{listholder.move1to2}" JD> move2to1="#{listholder.move2to1}"/> JD> Well, that's a lot of parameters, but there isn't really any way JD> around that - the tag needs to juggle two lists (which needs 4 JD> parameters), and two buttons (which needs two parameters). Jim, you're right about the facts: This component needs to have access to all the data/behavior you list in order to get its job done. However, one alternative is to pass in a single POJO with properties corresponding to the required data and behavior. That way, the tag could look like, for example: <ez:switchlist id="switchlist" controller=#{switchListController}" /> The interface declaration would then look like: <composite:interface name="switchlist" displayName="Switchlist Component" shortDescription="A basic example of the composite component feature"> <composite:attribute name="controller"> <composite:attribute name="selected1" required="true"/> <composite:attribute name="selected2" required="true"/> <composite:attribute name="items1" required="true"/> <composite:attribute name="items2" required="true"/> <composite:attribute name="move1to2" targets="move1to2" required="true" method-signature="void f1(javax.faces.event.ActionEvent)" /> <composite:attribute name="move2to1" targets="move2to1" required="true" method-signature="void f2(javax.faces.event.ActionEvent)" /> </composite:attribute> </composite:interface> You could also put "type" attributes on the non-method-signature attributes to make the contract more explicit. As you've discovered, the spec/implementation currently doesn't enforce the type attribute, but it could. The point I'm making is that it's trade-off in verbosity, but the complexity has to live somewhere. This fact tells me that the design of composite components has, at least in this narrowly defined example, reached Albert Einstein's goal of being "as simple as possible, but no simpler".

Yes, in the intervening 9 months, that changed.

Thanks for this sample. I'm testing it on Google App Engine with Majorra Beta 2 b14 and EL Parser from Glassfish and I must change #{compositeComponent.attrs.items2} to #{cc.attrs.items2}