Clear JSF form input values after submitting

If there's a form, and has a textbox and a button, how do you erase the content of the textbox after you submit the form?

<h:inputText id="name" value="#{bean.name}" />          
<h:commandButton id="submit" value="Add Name" action="#{bean.submit}" />

After I enter a value in the textbox and submit, the value still appears in the textbox. I need to clear the content of the textbox once its been submitted. How can I achieve this?


Solution 1:

Introduction

There are several ways to achieve this. The naive way is to simply null out the fields in backing bean. The insane way is to grab JS/jQuery for the job which does that after submit or even during page load. Those ways only introduces unnecessary code and indicates a thinking/design problem. All you want is just starting with a fresh request/page/view/bean. Like as you would get with a GET request.

POST-Redirect-GET

The best way is thus to just send a redirect after submit. You probably already ever heard of it: POST-Redirect-GET. It gives you a fresh new GET request after a POST request (a form submit), exactly as you intended. This has the additional benefit that the previously submitted data isn't re-submitted when the enduser ignorantly presses F5 afterwards and ignores the browser warning.

There are several ways to perform PRG in JSF.

  1. Just return to same view with faces-redirect=true query string. Assuming a /page.xhtml, you could do so in action method:

    public String submit() {
        // ...
    
        return "/page.xhtml?faces-redirect=true";
    }
    

    If you're still fiddling around with navigation cases the JSF 1.x way, then it's a matter of adding <redirect/> to the navigation case in question. See also How to make redirect using navigation-rule.

  2. To make it more reusable, you can obtain the view ID programmatically:

    public String submit() {
        // ...
    
        UIViewRoot view = FacesContext.getCurrentInstance().getViewRoot();
        return view.getViewId() + "?faces-redirect=true";
    }
    

    Either way, if you've view parameters which needs to be retained in the request URL as well, then append &includeViewParams=true to the outcome. See also Retaining GET request query string parameters on JSF form submit.

  3. If you're making use of some URL rewriting solution which runs outside JSF context, then you'd best grab the current request URL (with query string) and use ExternalContext#redirect() to redirect to exactly that.

    public void submit() throws IOException {
        // ...
    
        ExternalContext ec = FacesContext.getCurrentInstance().getExternalContext();
        StringBuffer requestURL = ((HttpServletRequest) ec.getRequest()).getRequestURL();
        String queryString = ((HttpServletRequest) ec.getRequest()).getQueryString();
        ec.redirect((queryString == null) ? requestURL.toString() : requestURL.append('?').append(queryString).toString());
    }
    

    It's only a mess which should really be refactored to some utility class.

Request/View scoped bean

Note that this all works only nicely in combination with request or view scoped beans. If you've a session scoped bean tied to the form, then the bean wouldn't be recreated from scratch. You've then another problem which needs to be solved as well. Split it into a smaller session scoped one for the session scoped data and a view scoped one for the view scoped data. See also How to choose the right bean scope?

Faces Messages

If you've a faces message to be shown as result of successful action, then just make it a flash message. See also How to show faces message in the redirected page.

public String submit() {
    // ...

    FacesContext context = FacesContext.getCurrentInstance();
    context.addMessage(clientId, message);
    context.getExternalContext().getFlash().setKeepMessages(true);
    return "/page.xhtml?faces-redirect=true";
}

Ajax

Only if you happen to have an ajax-only page on which a F5 would always trigger a fresh new GET request, then simply nulling out the model field(s) in action method shouldn't harm that much.

See also:

  • How to navigate in JSF? How to make URL reflect current page (and not previous one)
  • Pure Java/JSF implementation for double submit prevention

Solution 2:

You can blank out the property of the managed bean that should not be repainted when you render the response. This can be done done using code similar to the snippet posted below:

private String name;

public String getName(){return name;}
public void setName(String name){this.name=name};

public String submit()
{
    //do some processing
    ...
    // blank out the value of the name property
    name = null;
    // send the user back to the same page.
    return null;
}

The reason for the current behavior can be found in how the JSF runtime processes requests. All JSF requests to a view are processed in accordance with the JSF standard request-response lifecyle. In accordance with the lifecyle, the managed bean contents are updated with the value from request (i.e. the value of DataForm.Name is set) before the application event (DataForm.submit) is executed. When the page is rendered in the Render Response phase, the current value of the bean is used to render the view back to the user. Unless the value is changed in an application event, the value will always be one that is applied from the request.

Solution 3:

You can clear the form from the Bean method that gets called when the form is submitted;`

private String name;
    private String description;
    private BigDecimal price;

/*----------Properties ------------*/
/*-----Getter and Setter Methods---*/

public void save()throws SQLException{
String sql = "INSERT INTO tableName(name,description,price) VALUES (?,?,?)";
    Connection conn = ds.getConnection();
    try {

        PreparedStatement pstmt = conn.prepareStatement(sql);
        pstmt.setString(1, getName());
        pstmt.setString(2, getDescription());
        pstmt.setBigDecimal(3, getPrice());

        pstmt.executeUpdate();
    } catch (SQLException e) {
        e.getMessage();
        e.toString();
    }finally{
        conn.close();
        clear();
    }

}//End Save Method

public void clear(){
    setName(null);
    setDescription(null);
    setPrice(null);
}//end clear`

Notice that the clear() method is called from the save method after all the operations of the save method is complete. As an option you could perform the clearing only if the methods operation was successful...The method below is placed in the ProductController Class...

    public String saveProduct(){
    try {
        product.save(); 
    } catch (SQLException e) {
        e.printStackTrace();
    }
    return null;
}

The method call from the view/jsp would look like the Following:

<h:commandButton value="Save" action="#{productController.saveProduct}"/>

Solution 4:

You can do it with jQuery.

I had the similar problem. I needed to clear popup window form.

<rich:popupPanel id="newProjectDialog" autosized="true"
        header="Create new project">
        <h:form id="newProjectForm">
            <h:panelGrid columns="2">
                <h:outputText value="Project name:" />
                <h:inputText id="newProjectDialogProjectName"
                    value="#{userMain.newProject.projectName}"         required="true" />
                <h:outputText value="Project description:" />
                <h:inputText id="newProjectDialogProjectDescription"
                    value="#{userMain.newProject.projectDescription}"     required="true" />
            </h:panelGrid>
            <a4j:commandButton id="newProjectDialogSubmit" value="Submit"
                oncomplete="#{rich:component('newProjectDialog')}.hide();     return false;"
                render="projects" action="#{userMain.addNewProject}" />
            <a4j:commandButton id="newProjectDialogCancel" value="Cancel"
                onclick="#{rich:component('newProjectDialog')}.hide();     return false;" />
        </h:form>
        </rich:popupPanel>

jQuery code:

$('#newProjectForm').children('input').on('click', function(){$('#newProjectForm').find('table').find('input').val('');});