Simple ADF Client-Side Component Use Cases: Kaleidoscope ’09 Report IV

Last week, I talked about the essentials for doing any client-side component manipulation, as described in Lucas Jellema‘s ODTUG Kaleidoscope 2009 talk, “That’s Rich! Putting a smile on ADF Faces.” This week, I’m going to talk about a couple of simple use cases for client-side programming that he demonstrated.

Before I do that, though, I should mention what I think is currently the most important resource for client-side programming: Frank NimphiusJavaScript Programming Nuggets page. That contains a lot of tips about ADF Faces RC client-side programming, and goes into a considerably higher level of sophistication than Lucas’ talk did (or this post will). But in case you find that a bit intimidating to start out with, here are three very simple use cases for client-side programming.

Expanding a ShowDetail Component

ADF Faces RC components often come with various widgets that let you do things with them. For example, the ShowDetail component has a little box that you can use to show or hide the component’s contents. But what if you want to provide an alternative way of operating the component, like a “Show Details” button that expands the ShowDetail?

The “standard” way of doing this would be to use the Show Details  button to do a partial submit that re-renders the ShowDetail, something like the following:

<af:commandButton text="Show Details" id="showDetailsButton"
                  partialSubmit="true" immediate="true">
  <af:setPropertyListener from="#{true}"
                          to="#{pageFlowScope.state.detailShown}"
                          type="action"/>
</af:commandButton>
<af:showDetail disclosed="#{pageFlowScope.state.detailShown}" id="showDetail1"
               partialTriggers="showDetailsButton">
  <!-- details -->
</af:showDetail>

And that’s OK, of course, but it’s really a server round-trip for nothing; you can toggle a ShowDetail open or closed just fine on the client without mucking about with a server round-trip, managed bean, and component rerendering. Here’s the JSF code for a client-side solution:

<af:commandButton text="Show Details" id="showDetailsButton">
  <af:clientListener type="action" method="showDetailsNow"/>
</af:commandButton>
<af:showDetail disclosed="false" id="showDetail1"
               clientComponent="true">
  <!-- details -->
</af:showDetail>

And here’s the Javascript function:

function showDetailsNow(event) {
    var button = event.getSource();
    var showDetail1 = button.findComponent("showDetail1");
    showDetail1.setDisclosed(true);
    event.cancel();
}

The first two lines of the function body should be familiar from last week. The third line calls the (alas completely undocumented–as I ranted about here) setter method for the “disclosed” property on the showDetail1 object. (How did Lucas find this method? I’m not sure, but generally, experimenting with getters/setters for the properties you can set declaratively on the server-side component is a good idea). The last line is one that’s often important for client-side programming: It keeps the event from propagating to the server (since doing this without a server round-trip was the whole point of this in the first place).

A Simple Client-Side Calculation

This example is a perfect use case for client-side programming. You have a text field, in which the user can enter a Fahrenheit temperature, and, for informational purposes only, an output component that displays the same temperature in Celsius. In case you don’t remember, the conversion formula for this is c=(f-32)*5/9. Now, again, you could do this server-side using PPR. But for goodness’ sake, why would you ever bother doing that? A server hit for a trivial mathematical calculation, the result of which is displayed exclusively for the user’s convenience? Five years ago, nobody would ever have considered doing that anywhere but on the client, and they had the right idea. And it’s really not very hard at all. The JSF code:

<af:inputText value="#{bindings.Temperature.inputValue}"
              label="#{bindings.Temperature.hints.label}"
              required="#{bindings.Temperature.hints.mandatory}"
              columns="#{bindings.Temperature.hints.displayWidth}"
              maximumLength="#{bindings.Temperature.hints.precision}"
              shortDesc="#{bindings.Temperature.hints.tooltip}"
              id="fahrenheitInput">
  <f:validator binding="#{bindings.Temperature.validator}"/>
  <af:convertNumber groupingUsed="false"
                    pattern="#{bindings.DepartmentId.format}"/>
  <af:clientListener method="convertTemp" type="valueChange"/>
</af:inputText>
<af:outputText id="celsiusOutput" clientComponent="true"/>

And the Javascript:

function convertTemp(event) {
    var fahrenheitInput= event.getSource();
    var celsiusValue = (farenheitInput.getValue() - 32) * 5 / 9;
    var celsiusOutput = farenheitInput.findComponent("celsiusOutput");
    celsiusOutput.setValue(celsiusValue);
}

This time, we don’t even have to cancel the event, because value changes on text inputs, by default, don’t propagate to the server.

Showing/Hiding a Component

You likely know about the “rendered” property available on most JSF (and in particular ADF Faces RC components). If a component’s “rendered” property is false, the HTML for that component, and its descendents will never get generated on the server or sent to the client. You can set this property to an EL expression to render the component conditionally.

If you do this, and a component doesn’t get rendered, there’s no way to render it later (say, because a checkbox has been cleared) without a round-trip to the server. But there’s another, less well-known property that Lucas described, which gives you a lot more flexibility in client-side programming: “visible”. If a component’s “visible” property is false, the HTML for that component still gets generated and sent to the client–but inlined into chrome in such a way that it’s hidden to the user.You can dynamically, on the client, switch this “visible” property to true, causing the component to appear with no round-trip needed. For example:

<af:selectBooleanCheckbox value="#{bindings.UseBillingForShipping.inputValue}"
                          label="#{bindings.UseBillingForShipping.label}"
                          shortDesc="#{bindings.UseBillingForShipping.hints.tooltip}"
                          id="useBillCheckbox">
  <af:clientListener method="showHidePanel" type="click"/>
</af:selectBooleanCheckbox>
<af:panelHeader text="Shipping Address" id="shippingAddrPanel"
                visible="#{bindings.UseBillingForShipping.inputValue}"
                clientComponent="true">
  <!-- form goes here -->
</af:panelHeader>

The EL sets the visible property of the panelHeader when the component is first rendered, but then, we can manipulate it with no need to go back to the server:

function showHidePanel(event) {
    var useBillCheckbox = event.getSource();
    var shippingAddrPanel = useBillCheckbox.findComponent("shippingAddrPanel");
    shippingAddrPanel.setVisible(! useBillCheckbox.getSelected());
}

Nice, huh? Of course, if shippingAddrPanel is big and complex to render, and won’t usually be needed, you might want to do this the traditional way (with PPR), to avoid the rendering and download when it isn’t needed. But otherwise, for a bit of extra download time, you’ll quite significantly improve the performance of your checkbox this way.

Well. That’s four posts, and I’ve only covered Monday at Kaleidoscope. It was a pretty exciting conference, and I’m not done talking about it yet. But I think I’ll take a break from it for next week, and instead return to the long-abandoned series on ADF Business Components Tuning.