Better ajax operations and callbacks in JSF with PrimeFaces

One of the nicest feature of PrimeFaces is you can add some parameters to your ajax calls ( callbacks ) and can decide on what to update while executing your actions on your managed beans.

Think of following common scenario, we want to create a new user object with a dialog and show it in a datatable when user clicks on save button and do all these stuff with Ajax. Let’s think you as a developer want to do this using a p:dialog as follows:

<p:dialog  widgetVar="userFormDialog" >

   <p:outputPanel id="editDialog">
      <h:panelGrid>
         <h:outputLabel for="userName" value="Username" />
         <p:inputText id="userName"  
                        value="#{userManagementBean.user.username}" />
					
         <h:outputLabel for="password" value="Password"/>
         <p:inputText autocomplete="false" id="password"
                       value="#{userManagementBean.user.password}"/>
									
      </h:panelGrid>
   </p:outputPanel>

   <p:commandButton value="Save!" 
                       actionListener="#{userManagementBean.save}"
                       process="editDialog" update="userTable messages" 
                       oncomplete="userFormDialog.hide();"/>
</p:dialog>
                  
<p:messages id="messages"/>

<p:outputPanel id="userTable">
      <p:dataTable value="#{userManagementBean.all}" var="user" >
	<p:column>
          <f:facet name="header">Username</f:facet>
          #{user.username}
	</p:column>
     </p:datatable>
</p:outputPanel>

so what this code does it, when user clicks on save, the dialog should be closed, a message should be shown for successful creating and the p:datatable which shows all the users should be updated.

what happens if user just clicks save button w/o entering any data? what happens if the userName that the user entered is already in the database and saving the new one fails? what happens if there is a pb somewhere? When something unusual occurs, the dialog should stay opened and a message should be shown to the user right? This is the situation where PrimeFaces useful RequestContext API becomes handy.

RequestContext API enables developers to do 2 simple things. 1st you can tell what you want to update in the xhtml based on conditions in actions that you define in your Managed Beans.To update a component from serverside, you can just write:

RequestContext.getCurrentInstance().
              addPartialUpdateTarget("my_component_id");

so when the Ajax request is finished, the component with the id ‘my_component_id’ will be also updated. 2nd you can send parameters to your xhtml so that you can use these values in your javascript code. To pass an object you can use the following code :

RequestContext.getCurrentInstance().addCallbackParam("user", user);

as you can pass objects here, you can access them in your javascript methods in the client side.

So let’s define the requirement more precisely and try to implement it. We want to open a dialog when we want to create a new user for the system, the username and password are required fields and when user does not enter any data to these fields and click save, the dialog should stay visible and messages shown inside the dialog. Also for performance wise, if there is a validation pb, we should not update the datatable but update messages only. if the save is successful, then messages and datatable component should be updated and user should be notified by a message and alert. So our xhtml code will be something like this :

<p:dialog  widgetVar="userFormDialog" >

   <p:outputPanel id="editDialog">
      <h:panelGrid>
         <h:outputLabel for="userName" value="Username" />
         <p:inputText id="userName" value="#{userManagementBean.user.username}"  
                               required="true"/>
          <p:message for="userName"/>
					
         <h:outputLabel for="password" value="Password"/>
         <p:inputText autocomplete="false" id="password" value="#{userManagementBean.user.password}"/>
         <p:message for="password"/>
							
      </h:panelGrid>
   </p:outputPanel>

   <p:commandButton value="Save!" actionListener="#{userManagementBean.save}" process="editDialog"
				oncomplete="handleSaveRequest(xhr, status, args)"/>
</p:dialog>

<h:outputScript target="head">
      function handleSaveRequest(xhr, status, args) {  
           if( args.notValid || args.validationFailed )  
              return;
           userFormDialog.hide();  
           alert('User with username ' +args.user.username+ ' is saved successfully');
   }  
</h:outputScript>
                  
<p:messages id="messages"/>

<p:outputPanel id="userTable">
	<p:dataTable value="#{userManagementBean.all}" var="user" >
		<p:column>
		 <f:facet name="header">#{lbl['username']}</f:facet>
					#{user.username}
		</p:column>
	</p:datatable>
</p:outputPanel>

as you can see here at line 18, we defined a new js method to handle the response from the server. In this method ( starting at line 22 ) we check whether there is a validation error ( args.validationFailed is default set to true by PrimeFaces API ) or we had an error and send a parameter ( args.valid is set to true on server side with backing bean ). We also notified the user with an alert box and tell the user that a new user object is saved successfully by writing the username in the message. RequestContext API allows you to pass objects to js side with serializing them as JSON.

our backing bean will be like this :

@ViewScoped
@ManagedBean
public class UserManagementBean{
	private User user = new User();
	private List<User> users;

	public void save() {
		try{
			// some service to save user
 			// userService.save(user);
			addMessage( "User is Saved!");
			RequestContext.getCurrentInstance().
					addCallbackParam("user", user);
 			RequestContext.getCurrentInstance().
					addPartialUpdateTarget("userTable");
		}catch (SomeException e) {
			RequestContext.getCurrentInstance().
					addCallbackParam("notValid", true);
			addErrorMessage("Problem Occured!");
		}
		RequestContext.getCurrentInstance().
					addPartialUpdateTarget("messages");
	}

	public List<User> getAll() {
		return users;
	}

        // getter setter
}

As you can see that RequestContext API enables developers to decide on what to update and send parameter in Ajax request easily. You can do easier testing with RequestContext API as you can test whether the parameters and partial update request are done when there is an exception occurs or save operation is successful.