11 min read

 

The ajaxSingle and the process attributes

The ajaxSingle property is very useful to control the form submission when ajaxSingle is set to true—the form is not submitted and, just the Ajax component data is sent.

This attribute is available in every Ajax action component and we can use it to call an action from a button, skipping the form validation (like the JSF immediate property does), or to send the value of just an input into a form without validation and submitting the other ones.

The second use case can be used, for example, when we need an input menu that dynamically changes the value of other inputs without submitting the entire form:

<h:form>
<!-- other input controls -->
<h:selectOneMenu id="country"
value="#{myBean.selectedCountry}">
<f:selectItems value="#{myBean.myCountries}">
<a:support event="onchange" ajaxSingle="true"
reRender="city" />
</h:selectOneMenu>
<h:selectOneMenu id="city"
value="#{myBean.selectedCity}">
<f:selectItems value="#{myBean.myCities}">
</h:selectOneMenu>
<!-- other input controls -->
</h:form>

In this example, every time the user selects a new country, the value is submitted to the bean that recalculates the myCities property for the new country, after that the city menu will be re-rendered to show the new cities.

All that without submitting the form or blocking the changes because of some validation problem.

What if you would like to send more than one value, but still not the entire form?

We can use Ajax regions (we will see in the next sections), or we can use the process attribute. It contains a list of components to process while submitting the Ajax action:

<h:form>
<!-- other input controls -->
<h:inputText id="input1" ... />
<h:selectOneMenu id="country"
value="#{myBean.selectedCountry}">
<f:selectItems value="#{myBean.myCountries}">
<a:support event="onchange"
ajaxSingle="true"
process="input2, input3"
reRender="city" />
</h:selectOneMenu>
<h:inputText id="input2" ... />
<h:inputText id="input3" ... />
<h:selectOneMenu id="city"
value="#{myBean.selectedCity}">
<f:selectItems value="#{myBean.myCities}">
</h:selectOneMenu>
<!-- other input controls -->
</h:form>

In this example, we also wanted to submit the input2 and the input3 values together with the new country, because they are useful for retrieving the new cities list—just by setting the process attribute with the id list and during the submission, they will be processed. Thus input1 will not be sent.

Also, for action components such as buttons, you can decide what to send using the ajaxSingle and process attributes.

Form submission and processing
We speak about form “submission” to simplify the concept and make things more understandable. In reality, for every request, all of the form is submitted, but only the selected components (using ajaxSingle and/or process attributes) will be “processed”. By “processed” we mean “pass through” the JSF phases (decoding, conversion, validation, and model updating).

More Ajax!

For every contact, we would like to add more customizable fields, so let’s use the ContactField entity connected to every Contact instance.

First of all, let’s create a support bean called HomeSelectedContactOtherFieldsHelper inside the book.richfaces.advcm.modules.main package.

It might look like this:

@Name("homeSelectedContactOtherFieldsHelper")
@Scope(ScopeType.CONVERSATION)
public class HomeSelectedContactOtherFieldsHelper {

@In(create = true)
EntityManager entityManager;

@In(required = true)
Contact loggedUser;

@In
FacesMessages facesMessages;
@In(required = true)
HomeSelectedContactHelper homeSelectedContactHelper;

// my code
}

A notable thing is highlighted—we injected the homeSelectedContactHelper component, because to get the list of the customized fields from the database, we need the contact owner. We also set the required attribute to true, because this bean can’t live without the existence of homeSelectedContactHelper in the context.

Now, let’s add the property containing the list of personalized fields for the selected contact:

private List<ContactField> contactFieldsList;

public List<ContactField> getContactFieldsList() {
if (contactFieldsList == null) {
// Getting the list of all the contact fields
String query = "from ContactField cf
where cf.contact.id=:idContactOwner order by cf.id";
contactFieldsList = (List<ContactField>)
entityManager.createQuery(query)
.setParameter("idContactOwner",
homeSelectedContactHelper.getSelectedContact()
.getId()).getResultList();
}
return contactFieldsList;
}

public void setContactFieldsList(List<ContactField>
contactFieldsList) {
this.contactFieldsList = contactFieldsList;
}

As you can see, it is a normal property lazy initialized using the getter. This queries the database to retrieve the list of customized fields for the selected contact.

We have to put into the bean some other method useful to manage the customized field (adding and deleting field to and from the database), let’s add those methods:

public void createNewContactFieldInstance() {
// Adding the new instance as last field
(for inserting a new field)
getContactFieldsList().add(new ContactField());
}

public void persistNewContactField(ContactField field) {
// Attaching the owner of the contact
field.setContact(homeSelectedContactHelper.getSelectedContact());

entityManager.persist(field);
}

public void deleteContactField(ContactField field) {
// If it is in the database, delete it
if (isContactFieldManaged(field)) {
entityManager.remove(field);
}

// Removing the field from the list
getContactFieldsList().remove(field);
}

public boolean isContactFieldManaged(ContactField field) {
return field != null && entityManager.contains(field);
}

The createNewContactFieldInstance() method will just add a new (not yet persisted), empty instance of the ContactField class into the list.

After the user has filled the values in, he/she will press a button that calls the persistNewContactField() method to save the new data into the database.

In order to delete it, we are going to use the deleteContactField() method, and to determine if an instance is persisted into the database or not, we are going to use the isContactFieldManaged() method.

Now, let’s open the /view/main/contactView.xhtml file and add the code to show the personalized fields after h:panelGrid— i shows the standard ones:

<a:repeat value="#{homeSelectedContactOtherFieldsHelper.contactFieldsList}" 
var="field">
<h:panelGrid columns="2" rowClasses="prop"
columnClasses="name,value">
<h:outputText value="#{field.type} (#{field.label}):"/>
<h:outputText value="#{field.value}"/>
</h:panelGrid>
</a:repeat>

We are using a new RichFaces data iteration component that permits us to iterate over a collection and put the data we want (the rich:dataTable component would instead create a table for the elements list).

In our case, the h:panelGrid block will be repeated for every element of the collection (so for every customized field).

Now, let’s open the /view/main/contactEdit.xhtml file and add the code for editing the customized fields into the list:

<a:region>
<a:outputPanel id="otherFieldsList">
<a:repeat value="#{homeSelectedContactOtherFieldsHelper.
contactFieldsList}"
var="field">
<h:panelGrid columns="3" rowClasses="prop"
columnClasses="name,value,validatormsg">
<h:panelGroup>
<h:inputText id="scOtherFieldType"
value="#{field.type}"
required="true" size="5">
<a:support event="onblur"
ajaxSingle="true"/>
</h:inputText>

<h:outputText value=" ("/>
<h:inputText id="scOtherFieldLabel"
value="#{field.label}"
size="5">
<a:support event="onblur"
ajaxSingle="true"/>
</h:inputText>
<h:outputText value=")"/><br/>
<rich:message for="scOtherFieldType"
styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
</h:panelGroup>

<h:panelGroup>
<h:inputText id="scOtherFieldValue"
value="#{field.value}"
required="true">
<a:support event="onblur"
ajaxSingle="true"/>
</h:inputText><br/>
<rich:message for="scOtherFieldValue"
styleClass="messagesingle"
errorClass="errormsg"
infoClass="infomsg"
warnClass="warnmsg"/>
</h:panelGroup>

<h:panelGroup>
<a:commandButton image="/img/add.png"
reRender="otherFieldsList"
action="#{homeSelectedContactOtherFieldsHelper.
persistNewContactField(field)}"
rendered="#{!homeSelectedContactOtherFieldsHelper.
isContactFieldManaged(field)}">
</a:commandButton>

<a:commandButton image="/img/remove.png"
reRender="otherFieldsList" ajaxSingle="true"
action="#{homeSelectedContactOtherFieldsHelper.
deleteContactField(field)}">
</a:commandButton>
</h:panelGroup>
</h:panelGrid>
</a:repeat>

<a:commandLink reRender="otherFieldsList"
ajaxSingle="true"
action="#{homeSelectedContactOtherFieldsHelper.
createNewContactFieldInstance}"
rendered="#{homeSelectedContactHelper.
selectedContactManaged}"
styleClass="image-command-link">
<h:graphicImage value="/img/add.png"/>
<h:outputText value="#{messages['addNewField']}"/>
</a:commandLink>
</a:outputPanel>
</a:region>

The code looks very similar to the one in the view box, except for the action buttons (to add a new instance, persist, save, or delete) and, for the presence of the surrounding tag a:region (highlighted). This is very important in order to make sure the form works correctly; we will see why in the next section.

Also, notice that every input component has the a:support tag as a child that will update the bean with the edited value at the onblur event (which means that every time you switch the focus to another component, the value of the last one is submitted). So, if you delete or add a field, you will now loose the edited values for other fields. It is also used for Ajax validation, as the user is informed that the value is not valid when it moves the cursor to another input.

Here is a screenshot with the new feature in the edit box:

Build an Advanced Contact Manager using JBoss RichFaces 3.3: Part 3

Using a:support only for Ajax validation
If you want to use the a:support tag only for validation purpose, remember to set its bypassUpdates attribute to true, so the process would be faster as the JSF Update Model and Invoke Application phases will not be invoked.

Ajax containers

While developing a web application with RichFaces, it’s very useful to know how to use Ajax containers (such as the a:region component) in order to optimize Ajax requests.

In this section, we’ll discuss about the a:region component.

It is a very important component of the framework—it can define Ajax areas to limit the part of the component tree to be processed during an Ajax request.

Regions can be nested during an Ajax request and the closest one will be used.

By setting to true the a:region attribute called regionRenderOnly, you can use this component to limit the elements’ update—In this way, in fact, only the components inside the region can be updated.

Another important attribute is selfRendered; setting this to true tells the framework to render the response basing on component tree without referring to the page code—it is faster, but all of the transient elements that are not saved in the tree (such as f:verbatim or HTML code written directly without using JSF components) will be lost at the first refresh, so you can’t use them in this case.

To summarize, it is very useful to control the rendering process and optimize it, in order to limit the elements of a form to send during an Ajax request without validation problems, to show different indicators for Ajax status.

Example of using a:region:

<h:form>
<a:region>
<h:inputText id="it1" value="#{aBean.text1}">
<a:support event="onkeyup" reRender="text1" />
</h:inputText>
<h:inputText id="it2" value="#{aBean.text2}" />
</a:region>

<h:inputText id="it3" value="#{aBean.text3}" />

<a:commandButton
action="#{aBean.saveTexts}"
reRender="text1,text2" />

</h:form>

<h:outputText id="text1" value="#{aBean.text1}" />

<h:outputText id="text2" value="#{aBean.text2}" />

In this example, while the user is typing in the text1 value of inputText, a:support sends an Ajax request containing only the it1 and it2 values of inputText.

In this case, in fact, a:region limits the components sent by every Ajax request originated from inside the region. So, the Ajax request will only update aBean.text1 and aBean.text2.

Wrapping only a component inside an Ajax region is the equivalent of using the ajaxSingle property set to true.

If the user clicks on the a:commandButton aBean.text1, the aBean.text2 and aBean.text3 values will be updated by the Ajax request.

Coming back to our application, as all the customized fields are inside the same form component, we surround each one with the a:region tag. In this way, the single field is submitted regardless of the other ones.

For example, without using a:region, if the user empties the name input value and then tries to insert a new customized field, the process will fail because the name input is not validated. If we use the a:region component, the name field will not be processed and a new field will be inserted.

Now that we know how to use the a:region tag, we can combine it with ajaxSingle and process in order to decide what to send at every request, and to better optimize Ajax interactions into the application.

Build an Advanced Contact Manager using JBoss RichFaces 3.3: Part 3

LEAVE A REPLY

Please enter your comment!
Please enter your name here