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.

Advertisements

12 thoughts on “Better ajax operations and callbacks in JSF with PrimeFaces

  1. Hi Yiğit,
    congratulations for the great post!

    Just a question, is there any way to update only the component values rather than rerender the whole component?
    I mean, if after an ajax call the server has changed the model I want to update the values in the form but not to render the whole component

    Maybe I shouldn’t use the update attribute for doing that, right?

    Thanks in advance
    David

    • Hi,

      to be honest I didn’t get the whole idea, but if you want to update the model but do not want to do an update, you can use the process attribute, but not update.

      Yigit

  2. Hi again,
    sorry for misunderstanding.
    I’ll try to explain with an example: We have two fields in my xhtml page bound to two properties in my backing bean.
    When user types something in input field A and leaves it we make an ajax call to server. This call modifies the value for input text B.
    If I want to show the new value for input field B I have to do an update=”fieldB” in the fieldA’s p:ajax right?
    But i don’t want to rerender the whole component, I only want to change its value and I don’t know how to achieve that.

  3. RequestContext.getCurrentInstance() is returning null… do you have any idea why? I tried invoking my bean method both as an action and as an actionEvent and I have ajax=true.

  4. Thanks so much Mr.Yigit
    I got badly stucked with one problem, this post really saved me..
    Please keep post useful articles..
    We always supports you…

  5. public void onRowSelect(SelectEvent event)
    {
    String actionName = ON_ROW_SELECT;
    try
    {
    Student selectedObj = (Student)event.getObject();
    if (selectedObj != null)
    {
    selectedEditRec = selectedObj;
    }
    // if data is changed then show the dataChange dialog
    if (isDataChanged())
    {
    setShowDataChangedDialog(true);
    RequestContext context = RequestContext.getCurrentInstance();
    // execute javascript and show dialog
    context.execute(“PF(‘dataChangeDlg’).show();”);
    }

    }
    catch (Exception e)
    {
    handleException(e);
    }
    }

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s