Jim Driscoll's Blog

Notes on Technology and the Web

A final look at the Switchlist JSF 2 component

leave a comment »

It’s been a long time, but I’ve revisited the Switchlist component I blogged about here here and here. Read through those old entries to see where we are with things. I will assume you’re already familiar with the content of those entries, we’ve got a lot to cover as it is.


Today, we’re going to completely rework this code, to illustrate some best practices. We’ll make the component use Ajax, we’ll have it separate out the control from the model, and we’ll make the whole component addressable with an Ajax tag itself as well.


First, the change in the backing Java classes. Previously, I’d just tossed together all the of the necessary functions into a single monolithic Java class, which exposed four data properties (which means 8 getters and setters), as well as two action methods, used in an action listener. For obvious reasons, this is a sloppy design. Better to have the data separated out, with the control methods hidden from the enduser.


With that in mind, we’ll create an interface, let’s call it ListHolder. It looks like this:

public interface ListHolder {

    public String[] getList();

    public void setList(String[] list);

    public Map getItems();

}


This encapsulates the data that each list operates on. We’ll also make two classes that actually hold that data, like this:


@ManagedBean(name="listholder1")
@SessionScoped
public class ListHolder1 implements ListHolder, Serializable {

    private static final long serialVersionUID = -4047970327214634942L;

    String[] list = null;
    Map items = new LinkedHashMap();

    {
        items.put("one", "one");
        items.put("two", "two");
        items.put("three", "three");
        items.put("four", "four");
    }

    public String[] getList() {
        return list;
    }

    public void setList(String[] list) {
        this.list = list;
    }

    public Map getItems() {
        return items;
    }
}

Though we could use any class which implemented the interface. And lastly, we'll create a controller class:

@ManagedBean
@RequestScoped
public class SwitchlistController implements Serializable {

    private static final long serialVersionUID = -4002627066189080830L;

    ListHolder listholder1, listholder2;

    public String m1_2() {
        String[] list1 = listholder1.getList();
        Map<String, String> items2 = listholder2.getItems();
        Map<String, String> items1 = listholder1.getItems();
        if (list1 != null && list1.length > 0) {
            for (String item : list1) {
                items2.put(item, items1.remove(item));
            }
        }
        return null;
    }

    public String m2_1() {
        String[] list2 = listholder2.getList();
        Map<String, String> items2 = listholder2.getItems();
        Map<String, String> items1 = listholder1.getItems();
        if (list2 != null && list2.length > 0) {
            for (String item : list2) {
                items1.put(item, items2.remove(item));
            }
        }
        return null;
    }

    public void setListHolder1(ListHolder listholder1) {
        this.listholder1 = listholder1;
    }

    public void setListHolder2(ListHolder listholder2) {
        this.listholder2 = listholder2;
    }

}

Again, little here that's surprising - the m1_2 and m2_1 methods are now action methods, instead of actionListener methods, for reasons I'll go into in a minute. We also have two setter methods, which we'll use with setPropertyActionListener.


So, now we've got the model and the controller all laid out, let's see how this would be used in a view. Here's the tag we'd like to use in the using page:

<ez:switchlist id="switchlist"
   listholder1="#{listholder1}" 
   listholder2="#{listholder2}"/>

Two parameters, each pointing to the listholder classes that implement the listholder interface.


Now, let's look at the interface in the composite component itself:

<cc:interface name="switchlist">

    <cc:attribute name="listholder1" required="true">
        <cc:attribute name="list" required="true"/>
        <cc:attribute name="items" required="true"/>
    </cc:attribute>
    <cc:attribute name="listholder2" required="true">
        <cc:attribute name="list" required="true"/>
        <cc:attribute name="items" required="true"/>
    </cc:attribute>
</cc:interface>

What's here: Only the CompositeComponent version of the interface that we've defined earlier. For each of the two attribute, there are two properties - list and items.


And lastly, let's take a look at the implementation.

<cc:implementation>
    <h:outputStylesheet name="switchlist/switchlist.css"/>
    <div id="#{cc.clientId}">
    <h:selectManyListbox id="list1" value="#{cc.attrs.listholder1.list}" styleClass="switchlist">
        <f:selectItems value="#{cc.attrs.listholder1.items}"/>
    </h:selectManyListbox>
    <h:panelGroup id="buttonGroup" styleClass="switchlistButtons">
        <h:commandButton id="move1to2" value="&gt;&gt;" action="#{switchlistController.m1_2}"
                         styleClass="switchlistButton">
            <f:setPropertyActionListener value="#{cc.attrs.listholder1}" target="#{switchlistController.listHolder1}"/>
            <f:setPropertyActionListener value="#{cc.attrs.listholder2}" target="#{switchlistController.listHolder2}"/>
            <f:ajax execute="@this list1" render="list1 list2"/>
        </h:commandButton>
        <h:commandButton id="move2to1" value="&lt;&lt;" action="#{switchlistController.m2_1}"
                         styleClass="switchlistButton">
            <f:setPropertyActionListener value="#{cc.attrs.listholder1}" target="#{switchlistController.listHolder1}"/>
            <f:setPropertyActionListener value="#{cc.attrs.listholder2}" target="#{switchlistController.listHolder2}"/>
            <f:ajax execute="@this list2" render="list1 list2"/>
        </h:commandButton>
    </h:panelGroup>
    <h:selectManyListbox id="list2" value="#{cc.attrs.listholder2.list}" styleClass="switchlist">
        <f:selectItems value="#{cc.attrs.listholder2.items}"/>
    </h:selectManyListbox>
    </div>
</cc:implementation>


Now, top to bottom, let's look at what we've done here.


First, wrap everything in a div, and give it an id that's the same as the composite component as a whole. This is something you'll need to do to let ajax tags operate on your component - otherwise, there won't be anything in your page with the id of the component you just added - in our case "switchlist", in the using page. There would only be "switchlist:buttonGroup" and such. Doing this also means that you can better do styling with css, since you now have a block you can work with, instead of a loose group of tags with no wrapper.


Next, see that we have a bunch of references that look like #{cc.attrs.listholder1.list}. This is how we get access to the properties that we defined in the interface section, above, as well as in the ListHolder interface.


Also, note that we've changed from using an actionListener attribute on the buttons, to an action. We're also passing values into the controller via the f:setPropertyActionListener - these two things are related. If we'd just continued to use the actionListener method of executing a method, the setPropertyActionListeners wouldn't be called until after the actionListener - which makes sense, since those listeners are called in the order they're added, but not terribly useful for our purposes. So we instead use a action, which returns null - meaning no navigation.


Lastly, note that for making this ajax enabled, we simply need to say: f:ajax execute="@this list2" render="list1 list2". @this means to execute the button it's placed on, which calls the action method. Executing list2 (note the relative path, it uses the same semantics as UIComponent.findComponent()) is necessary for populating the contents of the items array. And lastly, the render command is necessary to show the results.


So, a brief recap:

  • Separate out your model from your controller. See how clean the code looks now?
  • You can nest attributes in the composite:interface section.
  • Once nested, you can access those values with dot notation.
  • Wrap your components in a top level div or span with the cc.clientId - you'll be glad later.
  • You can use setPropertyActionListener to pass params, but you'll need to use an action method. This is no different from standard JSF 1.2. Indeed, using the Ajax tag is usually just a matter of adding it to already working code. Neat!
  • @this refers the current component. (And, by the way @form refers the the wrapping form, and @all and @none are kinda self explanatory)

Lots of code in a short space, but we've already covered a lot in other blogs. Questions? Ask away, in the comments, and I'll do my best to answer.


And, one last note: If you're attending JavaOne, this example is the basis for a talk that Ryan Lubke and I will be giving at JavaOne - BOF 4146, on Wednesday, at 745pm, Esplanade 307. Come on by and ask questions. I'll post the slides and the full example code in a later blog, after the conference.

(This article originally published on my java.net blog on May 27, 2009.)

Advertisements

Written by jamesgdriscoll

February 9, 2010 at 8:52 PM

Posted in ajax, JSF

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: