Getting Started with ICEfaces in Workshop for WebLogic
Pages: 1, 2

The table should now look something like this:

<ice:dataTable rows="5"

               value="#{customers.customers}" 
               var="customer"
              id="custTable"
              sortColumn="#{customers.sortColumnName}"
              sortAscending="#{customers.sortAscending}">
  <h:column>
    <f:facet name="header">
      <ice:panelGroup>
        <ice:commandSortHeader columnName="id" arrow="true">
          <h:outputText value="Id"/>
        </ice:commandSortHeader>
      </ice:panelGroup>
    </f:facet>
    <h:outputText value="#{customer.id}"/>
  </h:column>
  <h:column>
    <f:facet name="header">
      <h:outputText value="First"/>
    </f:facet>
    <h:outputText value="#{customer.first}"/>
  </h:column>
  <h:column>
    <f:facet name="header">
      <ice:panelGroup>
        <ice:commandSortHeader columnName="LastName" arrow="true">
          <h:outputText value="Last"/>
        </ice:commandSortHeader>
      </ice:panelGroup>
    </f:facet>
    <h:outputText value="#{customer.last}"/>
  </h:column>
  ...
</ice:dataTable>

The last component of the view tab will be adding pagination to the table. Select the dataTable tag, and in the properties view, set the rows to 5 so that it will have a limited number of rows per page. Next, drag a dataPaginator tag below the data table. In the dialog, set the For field to the id of the table (in this case, custTable), and check the Paginator checkbox. (If this checkbox is left unchecked, this control can be used to display information like "Page 1 of 12" instead of buttons to enable switching pages.) Clicking the "..." button next to the Navigator attribute will let you specify either images or text for each of the controls (first, next, last, previous). After filling this out, the tag will look something like this:

<ice:dataPaginator id="custTableScroller"
                   for="custTable"
                   paginator="true">
  <f:facet name="first">
    <h:graphicImage style="border:none;"
                    url="/css/xp/css-images/arrow-first.gif"/>
  </f:facet>
  <f:facet name="previous">
    <h:graphicImage style="border:none;" 
                    url="/css/xp/css-images/arrow-previous.gif"/>
  </f:facet>
  ...
</ice:dataPaginator>

The images referenced here are copied directly into the webapp, but are also included in the runtime and could be referenced as ./xmlhttp/css/xp/css-images/x.gif. However, this does not work well with the Workshop JSP design view, since it does not know how to translate that path and find the images in the runtime jar, so you'll use local copies in the example so that the design view will work correctly.

Datatable
Figure 2. The customer data table in the design view

Again, the completed code is available project.zip. Now you can run the app again and try out sorting and pagination.

Adding Data Using an Asynchronous Request

The second tab will contain a form to add customers to the list. For this example, you'll just add the customers to a list maintained by the backing bean, in a real application this would save the data in a database or some other backing store. To demonstrate the asynchronous nature of ICEfaces, the data will be sent in an asynchronous request, and a modal dialog will be displayed to notify the user that the new customer was added.

In the second tab of the tabset, drag out a panelGrid tag, and make it two columns by three rows. The columns and rows are implicit, the children of a panelGrid is a list of panelGroup tags, and the row/column layout is determined based on the number of columns in the grid. For this case, each row will have a label and a text box, corresponding to the first and last name properties of the customer. You can add more rows to fill in the new customer's address by databinding to the customers.newCustomer.address.* properties. The last row will have a commandButton to actually save the data:

<ice:panelGrid columns="2" width="100%">
  <ice:panelGroup>
    <f:verbatim>First Name:</f:verbatim>
  </ice:panelGroup>
  <ice:panelGroup>
    <ice:inputText value="#{customers.newCustomer.first}"/>
  </ice:panelGroup>
  <ice:panelGroup>
    <f:verbatim>Last Name:</f:verbatim>
  </ice:panelGroup>
  <ice:panelGroup>
    <ice:inputText value="#{customers.newCustomer.last}"/>
  </ice:panelGroup>
  <ice:panelGroup>
    <ice:commandButton
           actionListener="#{customers.customerSaved}"
           value="Add"/>
  </ice:panelGroup>
  <ice:panelGroup></ice:panelGroup>
</ice:panelGrid>

The customers backing bean has a property newCustomer, which is a placeholder for a Customer object that is being added. The input text boxes here are data-bound to the properties on that object. The backing bean also has an action listener method customerSaved that will be invoked when the command button is clicked. This will "save" the new customer, and reset the newCustomer object so that the form will be cleared.

Customer Form
Figure 3. The customer input form in the design view

Now you just need to implement the dialog to notify the user that the customer was added. ICEfaces provides a panelPopup tag that uses styles to simulate a dialog in a regular HTML DOM. It has a visible attribute that can be data-bound to a property on a backing bean. The customers bean exposes a property that you can use here, that will be set to true when a user is added; it will be reset to false when the dialog is dismissed.

Because of the scoping of the modal dialog, it must be contained in its own form tag. So, first create a new form below the form that is wrapping the data table. Next, drag a panelPopup tag inside this form. In the properties view, bind the visible attribute to the popupVisible property of the customers backing bean. A panelPopup uses two facets— header and body—to set the title bar and body contents of the dialog. Put a message in each, and in the body, add a commandButton tag that will invoke the closeNewCustomerPopup action listener of the backing bean. The code should look something like this:

<ice:form>
  <ice:panelPopup visible="#{customers.popupVisible}"
                  modal="true">
    <f:facet name="header">
      <f:verbatim>Customer Added</f:verbatim>
    </f:facet>
    <f:facet name="body">
      <ice:panelGrid columns="1" width="100%">
        <ice:panelGroup>
          <ice:outputFormat
               value="Customer {0} {1} added with id {2}">
            <f:param value="#{customers.lastCustomer.first}"/>
            <f:param value="#{customers.lastCustomer.last}"/>
            <f:param value="#{customers.lastCustomer.id}"/>
          </ice:outputFormat>
        </ice:panelGroup>
        <ice:panelGroup>
          <ice:commandButton
               actionListener="#{customers.closeNewCustomerPopup}"
               value="Close"/>
        </ice:panelGroup>
      </ice:panelGrid>
    </f:facet>
  </ice:panelPopup>
</ice:form>

It will also be rendered in the design view, as shown in Figure 4.

Customer Dialog
Figure 4. The customer notification dialog in the design view

Run the app again and you should be able to add customers, and then switch back to the first tab to see the changes reflected in the data table without doing a full browser refresh. Figure 5 shows the input form.

Customer Form Runtime
Figure 5. The customer input form at runtime

Figure 6 shows the dialog that appears after a customer has been added.

Customer Dialog Runtime
Figure 6. The customer notification dialog at runtime

Troubleshooting

One thing to keep in mind is that because ICEfaces keeps a rendered DOM on the server, which gets updated during the JSF lifecycle, it is easy for this DOM to get out of sync during iterative development. When this happens, you will get errors on the server about illegal DOM hierarchy requests. Generally, you can republish the application, or, if that fails, restart the server, to get around these errors.

Another thing to watch out for when doing ICEfaces development is that all JSPs are valid XML. The ICEfaces parser will not accept any JSP directives.

 

One last issue with JSF in general is to make sure page requests are made with the appropriate extension—in this case, *.iface. If the request is made as *.jsp, then the ICEfaces servlet will not be invoked, and the JSP will not be executed correctly.

Wrapping Up

This example application should provide a good first look at what the ICEfaces framework can do. Many other controls are available in ICEfaces, as well as other techniques, such as partial form submits, that were not covered here.

Another way to get more familiar with ICEfaces is to take a plain JSF application, and replace the JSF HTML tags with their equivalent ICEfaces tags. In some cases, that simple step can give the application some Ajax functionality without any extra work. Several examples and tutorials are included in the ICEfaces distribution that can be imported into an ICEfaces-enabled webapp inside Workshop.

References

  • ICEfaces - The ICEfaces homepage
  • Firebug - The Firebug Firefox extension, useful for viewing Ajax requests and responses

Tom Stamm is a Staff Engineer on the Workshop team at BEA Systems, working on Beehive and Web Services tooling. Prior to Workshop, Tom contributed to several releases of WebLogic Portal.