Thursday, February 25, 2010

Use ice:commandButton to update UI fields bypassing validation

How to trigger an action that refreshes or updates ui input components bypassing JSF validation? Suppose you have a form with address fields where data can be entered by hand or copied from somewhere (a previously entered screen maybe) at the click of a button. You'd usually want to validate/convert these fields before data gets to its final destination in the backing bean.

You can use a command button to trigger a bean action that would copy data but the problem with this approach is that if there are errors (for example, if the user clicked Save with empty fields) on the screen JSF will prevent the button from submitting. You might try to set the button's immediate attribute to true which will force JSF to skip validation and ignore errors on UI components, but it will also mean that changes to data are not transferred to DOM and hence aren't seen by ICEfaces - you will see the action being triggered but the UI will not reflect the changes.

Using the immediate attribute is the simplest way to bypass validation but we need a way to get the new values from action to the UI. The simple trick is to bind the container where the UI data components reside to a backing bean property and clear it's children (the UI components) in the action method. This forces ICEfaces to fetch all data to rebuild components at which point it will see the changed values.

In short: make the command button immediate and bind the panel grid containing UI components with data to a backing bean property. In the action triggered by the button clear the grid to force ICEfaces to restore data. Here are the code snippets:


JSPX: put a container - a panel grid in this case - with ui fields:

<ice:panelGrid columns="4" binding = "#{backingBean.panelGrid}">
<!-- Add input components inside the grid>
</ice:panelGrid>


Add a command button:

<ice:commandButton value = "Copy Address"
immediate = "true"
action = "#{backingBean.copyAddress}"
actionListener="#{backingBean.copyAddressEvent}" />


Backing bean:

//Bind the grid to HtmlPanelGrid
public HtmlPanelGrid getPanelGrid() {
return panelGrid;
}

public void setPanelGrid(HtmlPanelGrid panelGrid) {
this.panelGrid = panelGrid;
}

//Do data updates and clear the grid
public void copyAddress(ActionEvent ae) {
thisAddress.streetAddress = otherAddress.streetAddress;
thisAddress.city = otherAddress.city;
getPanelGrid().getChildren().clear();
}

Friday, February 5, 2010

ice:panelCollapsible header styling issues

Had a problem this week with styling nested ICEfaces collapsible panels. (ice:panelCollapsible): the top panel would be styled, but the children's headers didn't get the neat open/close buttons from the default rime stylesheet. Custom styles wouldn't get recognized either.
After hours of digging around in CSS and ICE forums, I finally realized (thanks to the JIRA 3209) that the problem was not with the style, but with the layout. The content of header must be contained in a <div> (or an ice:panelGroup), otherwise the style doesn't get recognized. It doesn't have anything to do with nesting, I just happened to copy the top panel from elsewhere, and didn't pay attention to the wrapping panelGroup.


<ice:panelCollapsible expanded="true">
<f:facet name="header">
<div>
<ice:outputText value = "Grocery Panel"> </ice:outputText>
</div>
</f:facet>
<ice:panelGrid columns = "1">
<ice:outputText value = "Food stuffs"> </ice:outputText>
</ice:panelGrid>
<!-- Nested panel 1 -->
<ice:panelCollapsible>
<f:facet name="header">
<ice:panelGroup>
<ice:outputText value = "Fruit Panel"> </ice:outputText>
</ice:panelGroup>
</f:facet>
<ice:panelGrid columns = "2">
<ice:outputText value = "Apple"> </ice:outputText>
<ice:outputText value = "Orange"> </ice:outputText>
</ice:panelGrid>
</ice:panelCollapsible>
<!-- Nested panel 2 -->
<ice:panelCollapsible>
<f:facet name="header">
<ice:panelGroup>
<ice:outputText value = "Vegetable Panel"> </ice:outputText>
</ice:panelGroup>
</f:facet>
<ice:panelGrid columns = "2">
<ice:outputText value = "Cucumber"> </ice:outputText>
<ice:outputText value = "Potato"> </ice:outputText>
</ice:panelGrid>
</ice:panelCollapsible>
</ice:panelCollapsible>


Dev environment: ICEfaces 1.8.2, MyEclipse 7.5, Liferay 5.2.EE
 
1.