Friday, June 22, 2012

Dynamically add data at client side for JSF:updated

In my last blog, I showed a method how dynamically add a client row/list entry at client side when JSF is used.   I used Richfaces a4j:region and a4j:jsFunction. However, I do not want to use richfaces for two reasons. First, richfaces loads all its component js files although I only uses two simple tags. Second, richfaces loads its stylesheet file I have no easy way to turn it off completely.

Here I give a solution without richfaces

The Bean File (same as it is in last blog)

@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>
<ui:fragment>
<div id="#{component.clientId}" 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="_bufferbuttononclick="_displaybuffer(this, event); return false;" >
   <f:ajax listener="#{td.transferBuffer}" ></f:ajax>
</h:commandButton>
</div>
</ui:fragment>


Javascript using YUI

 _displaybuffer=function(button, event)
    {
        //do not submit the form, since we will make ajax call
        if (event!=null)
        {
            event.preventDefault();
            event.stopImmediatePropagation();
        }
        //event.
        var container=Y.one(button).ancestor("div._buffercontainer");
        var buffer=container.one("._bufferblock");
        if (buffer)
        {
            buffer.removeClass("_bufferblock");
        }  else
        {
            //collect execute id.
            var  names=[];
            container.all("input,select,textarea").each(function(node, index, list){
                names.push(node.get("name"));
            });
            //make the actual ajax call.
            options={
                    "javax.faces.behavior.event":"action", //a hack since the event in f:ajax does not work
                    onevent:function(data){
                        if (data.status=='success')
                        {
                           
                            //display one newly-loaded row.
                            //do not refer this button object since button will be replaced dynamically during reload
                            _displaybuffer("input[name='"+button.name+"']", null);
                        }
                    },
                    onerror:function(data)
                    {
                        window.alert(data.errorMessage);
                    },
                    render:container.get("id"),
                    execute:names.join(" ")
                   
                    };
            jsf.ajax.request(button, event, options);
        }
       
    };

Here I use code block for the onclick attribute of commandButton. This code will be called before the generated ajax flow from f:ajax. The generated ajax will not be called since the onclick code block always return false.  In the onclick code block, I use jsf.ajax.request to make explicit ajax call if no buffer is available.  JSF server does not distinguish whether the ajax call comes from manual javascript or the one generated. We still need the f:ajax tag so listener action can be invoked  in server side. 



No comments:

Post a Comment