Skip to main content

Auto-save JSF forms with Ajax: Part 1

Use XMLHttpRequest to submit JSF forms

Andrei Cioroianu, Senior Java Developer and Consultant, Devsphere
Andrei Cioroianu is a Senior Java Developer and Consultant at Devsphere, a provider of custom Java EE development and Ajax/JSF consulting services. You can reach Andrei through the contact form at www.devsphere.com.

Summary:  In this three-part series, author and Java™ developer Andrei Cioroianu shows you how to automatically save form data in a Java Web application using Asynchronous JavaScript + XML (Ajax) and JavaServer Faces (JSF) technologies. You'll learn how to submit Web forms with Ajax, how to use the JSF framework to handle Ajax requests, how to control the JSF request processing life cycle, how to manage form data on the server side, and how to identify anonymous users across browser sessions. Discover several frequently occurring development mistakes, including incorrect form-data encoding and improper Ajax request management, which can lead to failed requests and memory leaks.

View more content in this series

Date:  07 Aug 2007
Level:  Intermediate
Activity:  5014 views

Introduction

Many desktop applications let users save documents at any time, and many products automatically save documents being edited to minimize data loss if the application crashes. When users interact with Web applications, typically their data is saved only when the form is submitted to the server. Most Web applications don't allow users to save partially filled forms, close the browser, and resume the work later. In addition, if the user is suddenly disconnected because of a network problem, he/she cannot save data and some of their work might be lost.

The developerWorks Ajax resource center
Check out the Ajax Resource Center, your one-stop shop for information on the Ajax programming model, including articles and tutorials, discussion forums, blogs, wikis, events, and news. If it's happening, it's covered here.

Ajax is the ideal solution for solving these types of issues. When form data is sent with Ajax, the page doesn't need to be refreshed and the scroll position can remain unchanged, just as if you were working with a desktop application. Your users will appreciate the auto-saving capability, especially when they fill out complex forms or when there is a risk of data loss. For example, a Web support form should be saved automatically and periodically if users are testing a product while they fill out the support form for reporting problems. The users' systems might become unstable during the product testing and they might need to restart the computer several times before submitting the completed form. Form auto-saving can save time and potential data loss in such a case.

This first installment of the three-part series focuses on sending form data with Ajax and handling Ajax requests with JSF. It shows you the complete data flow that is implemented for auto-saving and covers how to get, encode, and submit form data in a Web browser using JavaScript. This article also shows how a JSF listener handles the submitted data on the server side, customizing the JSF request processing life cycle so that it can efficiently deal with Ajax requests. You can apply the techniques from this article in any Java Web application that is based on Ajax and JSF, whether you need form auto-saving or another similar feature.

When users interact with Web applications, typically their data is saved only when the form is submitted to the server. Most Web applications don't allow users to save partially filled forms, close the browser, and resume the work later. In addition, if the user is suddenly disconnected because of a network problem, he/she cannot save data and some of their work may be lost. Ajax is the ideal solution for solving these types of issues.

Obtaining the form data on the client side

This section presents a JSF form whose data is extracted in the Web browser using JavaScript and DOM. You can reuse the JavaScript code in your own applications with any Web forms. This sections also explains how to encode the form data properly to submit it to the server.

Building the JSF form

Let's take a quick look at a typical JSF example. The SupportForm.jsp page contains basic HTML components, such as input boxes, lists, radio buttons, a check box, and a submit button. All input components have their values bound to the properties of a JavaBean named SupportBean. The page's header contains a <script> tag importing the AutoSaveScript.js file (see Download). This JavaScript file contains a function named setAutoSaving() that is called within the onload attribute of the <body> tag to activate the form auto-saving after the Web browser loads the page. Listing 1 shows the partial source code of the SupportForm.jsp page:


Listing 1. The SupportForm.jsp page containing an example JSF form
                
<%@ taglib prefix="f" uri="http://java.sun.com/jsf/core" %>
<%@ taglib prefix="h" uri="http://java.sun.com/jsf/html" %>

<html>
<head>
<title>Support Form</title>
    <script src="AutoSaveScript.js">
    </script>
</head>
<body onload="setAutoSaving(10000)">

<f:view>

    <h1>Support Form</h1>

    <h:form id="supportForm">

        <p><h:outputText value="Name: "/>
        <h:message for="name"/><br>
        <h:inputText id="name" value="#{supportBean.name}"
                size="40" required="true">
        </h:inputText>
        ...
        <p><h:outputText value="Platform: "/>
        <h:message for="platform"/><br>
        <h:selectOneRadio id="platform" value="#{supportBean.platform}"
                layout="lineDirection" required="true">
            <f:selectItem itemValue="Windows" itemLabel="Windows"/>
            <f:selectItem itemValue="Linux" itemLabel="Linux"/>
            <f:selectItem itemValue="Mac" itemLabel="Mac"/>
        </h:selectOneRadio>
        ...
        <p><h:outputText value="Problem: "/>
        <h:message for="problem"/><br>
        <h:inputTextarea id="problem" value="#{supportBean.problem}"
                rows="10" cols="40" required="true"/>

        <p><h:commandButton id="submit" value="Submit"
            action="#{supportBean.submit}"/>

    </h:form>

</f:view>

</body>
</html>

When a user clicks a JSF link or enters a URL to open a JSF page, the Web browser builds an HTTP request and sends it to the Web server, which identifies the application containing the page and invokes the FacesServlet (configured in web.xml) to handle the request. After some context initialization, the page is executed and the JSF framework creates a component tree that mirrors the JSF tags used in the Web page. The renderers of those components generate the HTML code (shown in Listing 2), which contains the form elements:


Listing 2. The HTML code generated by the SupportForm.jsp page
                
<html>
<head>
<title>Support Form</title>
    <script src="AutoSaveScript.js">
    </script>
</head>
<body onload="setAutoSaving(10000)">

    <h1>Support Form</h1>

    <form id="supportForm" method="post" 
        action="/autosave/faces/SupportForm.jsp" 
        enctype="application/x-www-form-urlencoded">

        <p>Name: <br>
        <input id="supportForm:name" type="text" 
            name="supportForm:name" size="40" />
        ...
        <p>Platform: <br>
        <table id="supportForm:platform">
        <tr>
            <td><label><input type="radio" name="supportForm:platform" 
                value="Windows"> Windows</input></label></td>
            <td><label><input type="radio" name="supportForm:platform" 
                value="Linux"> Linux</input></label></td>
            <td><label><input type="radio" name="supportForm:platform" 
                value="Mac"> Mac</input></label></td>
        </tr>
        </table>
        ...
        <p>Problem: <br>
        <textarea id="supportForm:problem" name="supportForm:problem" 
            cols="40" rows="10">
        </textarea>

        <p><input id="supportForm:submit" type="submit" 
            name="supportForm:submit" value="Submit" />

        <input type="hidden" name="com.sun.faces.VIEW" 
            value="H4sIAA..." />
        <input type="hidden" name="supportForm" value="supportForm" />

    </form>

</body>
</html>

At the end of the HTML form, you will notice some hidden elements. These are internally used by the JSF implementation to identify the submitted form and to store the state of the component tree between requests if the javax.faces.STATE_SAVING_METHOD parameter is set to client in the web.xml file. From the browser's perspective, a JSF form is just like any other HTML form and you can use JavaScript and DOM to access the form's elements in the Web browser.

Getting and encoding the form data

The AutoSaveScript.js file contains a JavaScript function named getFormData(), which takes a form object and iterates over its elements to build a string containing the name-value pairs. This string follows the standard application/x-www-form-urlencoded format, separating the parameters with & and using = between the name and value of each parameter. The addParam() inner function encodes a single parameter, using the escape() function, which is provided by the JavaScript API. The escape() function replaces almost any non-alphanumeric ASCII character with % followed by the two-digit hexadecimal code of the encoded character. I say "almost any non-alphanumeric ASCII character" because there are a few other characters (such as +) that are included as unencoded along with the alphanumeric characters.

For example, if you pass the string a + b to escape(), you get a%20+%20b (20 is the hexadecimal code of the space character). This is a valid URL encoding as specified by RFC 1738, but if you submit the encoded string to a server-side script such as a JSP, you will get a space instead of the + character. This is also correct because RFC 1866, which describes the application/x-www-form-urlencoded format, states that space characters are encoded as + and any non-alphanumeric characters are replaced with % followed by the hexadecimal code. When the server decodes the string, any + character is reverted to a space.

In conclusion, the URL encoding performed by escape() is not exactly the same as application/x-www-form-urlencoded because escape() leaves the + characters unencoded while in the case of application/x-www-form-urlencoded, spaces can be encoded as + characters. The easiest way to solve this issue is to encode any + character as %2B (2B is the hexadecimal code of +). You can perform this operation on the result returned by escape(), using replace(). In this example, you would encode the string a + b as a%20%2B%20b. Listing 3 is the addParam() inner function, which adds an encoded name-value pair to the local dataString variable of getFormData():


Listing 3. Encoding a single request parameter
                
function getFormData(form) {
    var dataString = "";

    function addParam(name, value) {
        dataString += (dataString.length > 0 ? "&" : "")
            + escape(name).replace(/\+/g, "%2B") + "="
            + escape(value ? value : "").replace(/\+/g, "%2B");
    }
    ...
}

The getFormData() function gets the elements array of the form object and calls addParam(), depending on each element's type. A single name-value pair is added for every text box, password, or hidden field. The value of a check box or radio button is encoded only if the form element is checked. In the case of a list, a name-value pair is added for each selected option. Then getFormData() returns the string containing the form's encoded data (see Listing 4):


Listing 4. Getting and encoding the form data
                

function getFormData(form) {
    ...
    var elemArray = form.elements;
    for (var i = 0; i < elemArray.length; i++) {
        var element = elemArray[i];
        var elemType = element.type.toUpperCase();
        var elemName = element.name;
        if (elemName) {
            if (elemType == "TEXT"
                    || elemType == "TEXTAREA"
                    || elemType == "PASSWORD"
                    || elemType == "HIDDEN")
                addParam(elemName, element.value);
            else if (elemType == "CHECKBOX" && element.checked)
                addParam(elemName, 
                    element.value ? element.value : "On");
            else if (elemType == "RADIO" && element.checked)
                addParam(elemName, element.value);
            else if (elemType.indexOf("SELECT") != -1)
                for (var j = 0; j < element.options.length; j++) {
                    var option = element.options[j];
                    if (option.selected)
                        addParam(elemName,
                            option.value ? option.value : option.text);
                }
        }
    }
    return dataString;
}


Submitting the form data with Ajax

This section shows how to send the form data to the JSF page using Ajax and also covers related topics, such as error handling and the correct management of the XMLHttpRequest objects, which you need to know to prevent memory leaks in the Web browser.

Creating and sending Ajax requests

The submitFormData() function, whose code you can find in the AutoSaveScript.js file (see Download), uses an Ajax request object to submit the encoded data to the Web server. First of all, it needs to create the request object using ActiveXObject() for Microsoft® Internet Explorer or the XMLHttpRequest() constructor for all other Ajax-capable browsers, such as Firefox, Netscape, Mozilla, Opera, and Safari. Listing 5 shows the code that creates the XMLHttpRequest object:


Listing 5. Creating an Ajax request object
                
function submitFormData(form) {
    var xhr;
    if (window.ActiveXObject)
        xhr = new ActiveXObject("Microsoft.XMLHTTP");
    else if (window.XMLHttpRequest)
        xhr = new XMLHttpRequest();
    else
        return null;
    ...
}

The form's encoded data is submitted to the page identified by the action URL of the form, using the specified HTTP method, which is POST in the case of a JSF form. The submitFormData() function can also be used with non-JSF forms that may use the default GET method or POST. The code would work even if the form doesn't specify an action URL. In this case, submitFormData() would use the current page's URL, which is obtained with document.URL. The encoded form data is retrieved from the form object, using the getFormData() function, which was presented in the previous section. If the HTTP method is GET, the encoded data is appended to the URL after a ? character. Then, submitFormData() initializes the xhr object, using its open() method (see Listing 6):


Listing 6. Initializing the Ajax request object
                
function submitFormData(form) {
    ...        
    var method = form.method ? form.method.toUpperCase() : "GET";
    var action = form.action ? form.action : document.URL;
    var data = getFormData(form);

    var url = action;
    if (data && method == "GET")
        url += "?" + data;
    xhr.open(method, url, true);
    ...
}

An inner function named submitCallback() is invoked when the response to the Ajax request is received from the server. This Ajax callback signals an error if autoSaveDebug is true, the request is completed (readyState is 4), but its status isn't OK (status is not 200). In case of an error, the status and statusText properties of the xhr object are reported with alert() and the autoSaveDebug flag is set to false so that you don't get the error message again and again (because the form saving is performed periodically). If you reload the page, however, the JavaScript code will be reinitialized and you'll see the error message again if the problem that generated the HTTP error still hasn't been fixed. This behavior is suitable for debugging during the development phase. In a production environment, it would be better to redirect the user to another page instead of showing the alert message. In any case, the onreadystatechange property of the xhr object must contain a reference to submitCallback() so that the callback function can be invoked during the life cycle of the Ajax request (see Listing 7):


Listing 7. The callback function
                
var autoSaveDebug = true;

function submitFormData(form) {
    ...        
    function submitCallback() {
        if (autoSaveDebug && xhr.readyState == 4 
                && xhr.status != 200) {
            autoSaveDebug = false;
            alert("Auto-Save Error: "
                + xhr.status + " " + xhr.statusText);
        }
    }
    xhr.onreadystatechange = submitCallback;
    ...
}

Next, submitFormData() sets the Ajax-Request header, which is specific to the sample application and is used to identify the Ajax requests on the server side as you'll see later in the article. If the HTTP method is POST, the submitFormData() function sets the standard Content-Type header and submits the form data to the JSF page, using the send() method of the xhr object. If the HTTP method is GET (currently not used by JSF forms), the form data has already been appended to the URL and send() is called with a null parameter. After submitting the form data, the function returns a reference to the xhr object (see Listing 8):


Listing 8. Sending the Ajax request
                
function submitFormData(form) {
    ...        
    xhr.setRequestHeader("Ajax-Request", "Auto-Save");
    if (method == "POST") {
        xhr.setRequestHeader("Content-Type",
            "application/x-www-form-urlencoded");
        xhr.send(data);
    } else
        xhr.send(null);
    
    return xhr;
}

Managing the XHR instances

When sending the form data repeatedly, you might be tempted to reuse the XMLHttpRequest (XHR) objects, but in most cases this is a bad idea for several reasons. First, it would complicate your code because you would have to manage a pool and track the life cycle of the XHR instances. Keep in mind that the state of these objects is typically accessed after the HTTP request is completed and the application code would have to signal to the code managing the pool when each XHR isn't needed anymore. Also, some browsers seem to have problems reusing XHR objects for multiple requests.

Creating a new XHR object per HTTP request poses another problem because the browser won't delete these objects from the memory as long as the application might need them. A Web page that sends Ajax requests from time to time would cause a memory leak in the Web browser if the application doesn't free the memory occupied by the XHR objects using the JavaScript delete operator. A good strategy that works in many cases is to abort a request and resend it if you don't get a response in a reasonable time frame. Both the server and the client must be prepared to get Ajax requests and responses out of order, and some of them might even be missed. If this isn't acceptable, you should probably use XMLHttpRequest to send synchronous requests.

Applications can use Ajax for auto-saving, but your forms should either provide good old submit buttons or use synchronous requests for sending the data for processing. The unreliable nature of the asynchronous requests is acceptable for auto-saving because this is implemented only to partially recover from a browser crash or network failure.

The AutoSaveScript.js file contains a function named submitAllForms(), which sends the data of all forms of the Web page. The XHR objects are kept in an array that allocates one element for each form. Before sending the data of a form with submitFormData(), the submitAllForms() function aborts and deletes the request that was used for the previous auto-saving of the form. Calling abort() on a successfully completed request won't do anything and onreadystatechange is set to an empty function just in case the browser gets a delayed response for an old request. Listing 9 shows the code that iterates over the forms of the page, submitting their data to the server:


Listing 9. Submitting all forms of the current page
                
var autoSaveXHR = new Array(document.forms.length);

function submitAllForms() {
    var formArray = document.forms;
    for (var i = 0; i < formArray.length; i++) {
        if (autoSaveXHR[i]) {
            var oldXHR = autoSaveXHR[i];
            oldXHR.onreadystatechange = function() { };
            oldXHR.abort();
            delete oldXHR;
        }
        autoSaveXHR[i] = submitFormData(formArray[i]);
    }
}

The XHR objects returned by submitFormData() will be deleted the next time submitAllForms() is called. Another function of the AutoSaveScript.js file is setAutoSaving(), which enables the form auto-saving by using the setInterval() function of the JavaScript API. The browser will call submitAllForms() every time the specified number of milliseconds elapses until setAutoSaving() is used again to clear the effects of the previous call with clearInterval() (see Listing 10):


Listing 10. Enabling form auto-saving for the current page
                
var autoSaveIntervalId = null;

function setAutoSaving(millisec) {
    if (autoSaveIntervalId) {
        clearInterval(autoSaveIntervalId);
        autoSaveIntervalId = null;
    }
    if (millisec != 0)
        autoSaveIntervalId = setInterval(
                "submitAllForms()", millisec);
}


Using a JSF listener for handling Ajax requests

So far, you've learned how to submit form data to a JSF page with Ajax. Now let's see how to handle Ajax requests on the server side. This starts with a brief description of the JSF request processing life cycle, containing all you need to know to understand the sample code included with this article. The JSF specification contains a complete presentation of the request processing life cycle, which you'll probably find very useful when developing your own JSF-based applications.

Understanding the JSF request processing life cycle

When processed by the JSF framework, a typical request that posts some form data goes through six phases:

  1. Restore View
  2. Apply Request Values
  3. Process Validations
  4. Update Model Values
  5. Invoke Application
  6. Render Response

First, the framework needs to restore the component tree of the form page. Depending on the value of the javax.faces.STATE_SAVING_METHOD configuration parameter, the component tree may be deserialized from a request parameter, or it may be obtained from the HttpSession object.

Then, the JSF framework traverses the tree recursively, updating the state of the components. For example, an input component that implements EditableValueHolder will have its submittedValue property set to the corresponding request-parameter. If the immediate property of the component is true, the JSF framework also converts and validates the submitted value, setting the value property of the component. If the immediate property is false, the conversion and validation are performed during the following phase of the JSF request processing life cycle.

After the first three phases (Restore View, Apply Request Values, and Process Validations), the component tree contains submitted form data, which has been decoded, converted, and validated by the JSF framework. At this point, the application should save the values of the JSF components to be able to restore the data of the Web form later.

The processing life cycle of an Ajax request that auto-saves form data must be stopped after the Validation phase. Otherwise, the JSF framework would go to the next phase, updating the model values of the JavaBean properties that are bound to the JSF components. In the case of a normal form submit that occurs when the user clicks a button, the JSF framework would also invoke the action method associated with the command button. The final phase renders the HTML response. The last three phases of the request processing life cycle (Update Model Values, Invoke Application, and Render Response) are not needed when the form is auto-saved.

Implementing the PhaseListener interface

Form auto-saving should not interfere with the functionality of the application. This means no JavaBean properties should be set and no action method should be invoked when the form is submitted with Ajax for auto-saving purposes. In addition, no HTML response should be generated after an auto-save because the browser doesn't need any refresh. Therefore, the application needs to control the JSF request processing life cycle, which can be easily done by implementing a PhaseListener. In the sample application included with this article, the JSF listener class is named AutoSaveListener and is configured in the faces-config.xml file (see Listing 11):


Listing 11. Configuring the JSF phase listener
                
<faces-config>

    <lifecycle>
        <phase-listener>autosave.AutoSaveListener</phase-listener>
    </lifecycle>
    ...

</faces-config>

As explained earlier, processing the auto-saving requests must be stopped after the validation phase so that the JSF framework doesn't update the JavaBean data model, which should remain unaffected by the auto-saving. Therefore, the listener is interested in receiving notifications only after the validation phase, whose ID is returned by the getPhaseId() method. If the request is marked with the Ajax-Request header, the listener's afterPhase() method invokes the responseComplete() method of the FacesContext object, signaling the JSF framework that it should stop processing the request (see Listing 12):


Listing 12. Implementing the JSF phase listener
                
package autosave;

import javax.faces.component.EditableValueHolder;
import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import java.util.Iterator;
import java.util.Map;

public class AutoSaveListener implements PhaseListener {

    public PhaseId getPhaseId() {
        return PhaseId.PROCESS_VALIDATIONS;
    }
    
    public void beforePhase(PhaseEvent e) {
    }

    public void afterPhase(PhaseEvent e) {
        System.out.println();
        System.out.print(e.getPhaseId());
        System.out.print(" - ");
        FacesContext ctx = e.getFacesContext();
        Map headers = ctx.getExternalContext().getRequestHeaderMap();
        if ("Auto-Save".equals(headers.get("Ajax-Request"))) {
            System.out.println("Auto-Save");
            ctx.responseComplete();
        } else
            System.out.println("Submit");
        printTree(ctx.getViewRoot(), 0);
    }
    ...
}

Printing the component tree

In addition to stopping the request processing at the right time, the listener prints the component tree using a recursive method named printTree(). This method outputs the component family, the renderer type, the unique id, and the validated value of each component (see Listing 13):


Listing 13. Printing the JSF component tree
                
public class AutoSaveListener implements PhaseListener {
    ...
    public void printTree(UIComponent comp, int level) {
        if (comp == null)
            return;
        Object value = null;
        if (comp instanceof EditableValueHolder)
            value = ((EditableValueHolder) comp).getValue();

        for (int i = 0; i < level; i++)
            System.out.print("    ");
        System.out.print(comp.getFamily());
        System.out.print(" - ");
        System.out.print(comp.getRendererType());
        System.out.print(" - [");
        System.out.print(comp.getId());
        System.out.print("]");
        if (value != null) {
            System.out.print(" - ");
            if (value instanceof Object[]) {
                Object array[] = (Object[]) value;
                for (int i = 0; i < array.length; i++) {
                    System.out.print(array[i]);
                    System.out.print(" ");
                }
            } else
                System.out.print(value);
        }
        System.out.println();

        Iterator children = comp.getChildren().iterator();
        while (children.hasNext()) {
            UIComponent child = (UIComponent) children.next();
            printTree(child, level + 1);
        }
    }

}

Listing 14 shows how the printed component tree looks:


Listing 14. The printed component tree
                
PROCESS_VALIDATIONS 3 - Auto-Save
javax.faces.ViewRoot - null - [null]
    javax.faces.Form - javax.faces.Form - [supportForm]
        javax.faces.Output - javax.faces.Text - [_id0]
        javax.faces.Message - javax.faces.Message - [_id1]
        javax.faces.Input - javax.faces.Text - [name] - John Smith
        ...
        javax.faces.Output - javax.faces.Text - [_id11]
        javax.faces.Message - javax.faces.Message - [_id12]
        javax.faces.SelectOne - javax.faces.Radio - [platform] - 
                                                           Windows
            javax.faces.SelectItem - null - [_id13]
            javax.faces.SelectItem - null - [_id14]
            javax.faces.SelectItem - null - [_id15]
        ...
        javax.faces.Output - javax.faces.Text - [_id26]
        javax.faces.Message - javax.faces.Message - [_id27]
        javax.faces.Input - javax.faces.Textarea - [problem] - 
                                                     Unable to ...
        javax.faces.Command - javax.faces.Button - [submit]

By getting the values of the input components that implement the EditableValueHolder interface, the printTree() method actually obtains the submitted form data from the component tree. In Part 2 of this series, you'll find more JSF techniques for working with the component tree and controlling the request processing life cycle.

Conclusion

This article has taught you how to encode and submit form data with Ajax and how to implement a JSF listener that retrieves submitted data from the JSF component tree. Stay tuned for the second part of this series, where you will see how to manage form data on the server side and how to restore the JSF form after the user closes and reopens the browser. The solution that Part 2 presents works for a multiuser and a multiform application and provides a method for identifying anonymous users across browser sessions.



Download

DescriptionNameSizeDownload method
Sample application for this articlewa-aj-jsf1.zip9KB HTTP

Information about download methods


Resources

Learn

Get products and technologies

Discuss

About the author

Andrei Cioroianu is a Senior Java Developer and Consultant at Devsphere, a provider of custom Java EE development and Ajax/JSF consulting services. You can reach Andrei through the contact form at www.devsphere.com.

Comments (Undergoing maintenance)



Trademarks  |  My developerWorks terms and conditions

Help: Update or add to My dW interests

What's this?

This little timesaver lets you update your My developerWorks profile with just one click! The general subject of this content (AIX and UNIX, Information Management, Lotus, Rational, Tivoli, WebSphere, Java, Linux, Open source, SOA and Web services, Web development, or XML) will be added to the interests section of your profile, if it's not there already. You only need to be logged in to My developerWorks.

And what's the point of adding your interests to your profile? That's how you find other users with the same interests as yours, and see what they're reading and contributing to the community. Your interests also help us recommend relevant developerWorks content to you.

View your My developerWorks profile

Return from help

Help: Remove from My dW interests

What's this?

Removing this interest does not alter your profile, but rather removes this piece of content from a list of all content for which you've indicated interest. In a future enhancement to My developerWorks, you'll be able to see a record of that content.

View your My developerWorks profile

Return from help

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Web development, Java technology
ArticleID=245496
ArticleTitle=Auto-save JSF forms with Ajax: Part 1
publish-date=08072007
author1-email=andcio@gmail.com
author1-email-cc=

My developerWorks community

Tags

Help
Use the search field to find all types of content in My developerWorks with that tag.

Use the slider bar to see more or fewer tags.

Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere).

My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Use the search field to find all types of content in My developerWorks with that tag. Popular tags shows the top tags for this particular content zone (for example, Java technology, Linux, WebSphere). My tags shows your tags for this particular content zone (for example, Java technology, Linux, WebSphere).

Special offers