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

Exclusive offer: get 50% off this eBook here
JBoss RichFaces 3.3

JBoss RichFaces 3.3 — Save 50%

Enhance your JSF web applications using powerful AJAX components

$23.99    $12.00
by Demetrio Filocamo | October 2009 | Java Open Source Web Development

Read Build an Advanced Contact Manager using JBoss RichFaces 3.3: Part 2 here.

 

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

JBoss RichFaces 3.3 Enhance your JSF web applications using powerful AJAX components
Published: October 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

Data pagination with data iteration components

As for h:dataTable (and rich:dataTable), you can attach datascroller to every data iteration component the same way you did for your dataTable:

<h:form>
<rich:dataList id="contactsList"
value="#{homeContactsListHelper.contactsList}"
var="contact"
rows="3">
<h:outputText value="#{contact.name} #{contact.surname}"/>
</rich:dataList>
<rich:datascroller for="contactsList" />
</h:form>

Just remember to put the datascroller into form, set the for attribute, and set the rows attribute for one of the data iteration components.

The result is as follows:

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

Also, in this case, there is a little exception for the rich:dataGrid component, as it doesn't have the rows attribute, but the corresponding one is elements (you can figure out why). Therefore, our example will be:

<h:form>
<rich:dataGrid id="contactsGrid"
value="#{homeContactsListHelper.contactsList}"
var="contact"
columns="3"
elements="3">
<rich:panel>
<f:facet name="header">
<h:outputText value="Contact" />
</f:facet>
<h:outputText
value="#{contact.name} #{contact.surname}"/>
</rich:panel>
</rich:dataGrid>
<rich:datascroller for="contactsGrid" />
</h:form>

And the result would appear as follows:

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

Addresses management

Another piece we would like to implement is addresses management using the ContactAddress entity. This is the same working mechanism as the customized field, so it is left as an exercise for the reader.

However, you can find the developed feature in the source code of the application.

Some screenshots

Here, we present some screenshots of the application with all the features we've seen uptil now.

We can see the Contacts list with a contact selected:

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

The contact editing form (you can notice the buttons to add/delete more fields and addresses) appears as follows:

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

And finally, you can see the add contact form:

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

Summary

In this article, we've developed the core feature of our application—contact management. We have learned about Ajax interaction and containers and about new Ajax components that RichFaces offers.

JBoss RichFaces 3.3 Enhance your JSF web applications using powerful AJAX components
Published: October 2009
eBook Price: $23.99
Book Price: $39.99
See more
Select your format and quantity:

About the Author :


Demetrio Filocamo

Demetrio Filocamo is a computer science engineer with more then 10 years of experience on both Desktop and Web applications development. He works as a freelance in London and collaborates with some companies and universities in Italy. Demetrio has been developing Enterprise Java Applications using open source solutions for the last five years.

Books From Packt

JBoss AS 5 Development
JBoss AS 5 Development

jQuery 1.3 with PHP
jQuery 1.3 with PHP

Joomla! with Flash
Joomla! with Flash

Drools JBoss Rules 5.0 Developer's Guide
Drools JBoss Rules 5.0 Developer's Guide

JBoss Tools 3 Developers Guide
JBoss Tools 3 Developers Guide

Tomcat 6 Developer's Guide
Tomcat 6 Developer's Guide

Apache MyFaces Trinidad 1.2: A Practical Guide
Apache MyFaces Trinidad 1.2: A Practical Guide

jQuery UI 1.6: The User Interface Library for jQuery
jQuery UI 1.6: The User Interface Library for jQuery

 

 

 

No votes yet
Thank you by
It's a greate book :) :) :). Thanks so much :)

Post new comment

CAPTCHA
This question is for testing whether you are a human visitor and to prevent automated spam submissions.
d
G
b
u
3
Z
Enter the code without spaces and pay attention to upper/lower case.
Code Download and Errata
Packt Anytime, Anywhere
Register Books
Print Upgrades
eBook Downloads
Video Support
Contact Us
Awards Voting Nominations Previous Winners
Judges Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software
Resources
Open Source CMS Hall Of Fame CMS Most Promising Open Source Project Open Source E-Commerce Applications Open Source JavaScript Library Open Source Graphics Software