Friday, June 22, 2012

Dynamically add data at client side for JSF

You have a list or a table. You want to add more items to the collection at client side. When user clicks a 'Add' button, a new row or list entry is available for user to enter new data.  The  list entry or row can be generated by manipulate DOM using JavaScript. However, the newly-added form elements in the row or list entry will be ignored by JSF server code since these elements are not in the JSF component tree.

Here I give a practical solution to this. The idea is like this: I have a real list and buffer list. The real list is visible at UI and the buffer list is hidden. When user clicks 'Add', an list entry from buffer list is displayed using JavaScript.
When the form is submitted, the real list and buffer list is combined.
If user repeatedly clicks 'Add' and buffer exhausts, Ajax call is made to server to reallocate buffer. If the buffer is reasonable large enough, there will not be any Ajax call in most time.

The Bean File

@ManagedBean
class Bean
{
List<Integer> real=new ArrayList<Integer>();
List<Integer> buffer=new ArrayList<Integer>(10);

public List<Integer> getReal()
{
   return real;
}

public List<Integer> getBuffer()
{
   return buffer;
}

//ajax call AND form submission time.
public void transferBuffer()
{
  for(Integer i:buffer)
  {
    real.add(i);
  }
  buffer=new ArrayList<Integer>(10);
}
 
}


xhtml file

<style>
 ._bufferblock
{
display:none;
}
</style>
<a4j:region>
<div class="_buffercontainer">
<table >

<ui:repeat value="#{bean.real}" var="item">
<tr><td> <h:inputText value="#{item}"></inputText></td></tr>
</ui:repeat> 

<ui:repeat value="#{bean.buffer}" var="item">
<tr class="_bufferblock"><td> <h:inputText value="#{item}"></inputText></td></tr>
</ui:repeat>
 </table>

<h:commandButton value="Add" styleClass="_bufferbutton" ></h:commandButton>
<a4j:jsFunction name="_reloadbuffer" render="@region" execute="@region"
                    actionListener="#{bean.transferBuffer}"></a4j:jsFunction>
</div>
 </a4j:region>

Javascript using jQuery

 jQuery(":button._bufferButton").live("click", function(event){
        event.preventDefault();
        var buffer=jQuery("._bufferblock:first", jQuery(this).closest("div._buffercontainer"));
        if (buffer.length>0)
        {
           //display it.
            buffer.removeClass("_bufferblock");
        } else
        {
            //reallocate buffer.
            _reloadbuffer();
        }
    });

NOTE

  • In JavaScript, I use live instead of click since the button is dynamically removed and added by JSF ajax call.

  • I use a4j and h:commandButton instead of f:ajax inside of h:commandButton. A f:ajax  will add onclick attribute to the generated button. The onclick javascript invokes JSF ajax call. Any other event listeners added later using javascript will be invoked after this. Right now, we have no way to invoke an event handler before the f:ajax client behaviour. So I separate them into two components to control the invocation flow as desired. 

  • a4j:region and a4j:jsFunction is from Jboss richfaces library .

 


No comments:

Post a Comment