Search code examples
ajaxjsf-2mojarra

Why is this ajax @form submit not updating and rendering model values?


I have the following scenario and a problem with a form submit using ajax:

<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE html
        PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
        "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml"
    xmlns:h="http://java.sun.com/jsf/html"
    xmlns:f="http://java.sun.com/jsf/core" xml:lang="en" lang="en"
    xmlns:ui="http://java.sun.com/jsf/facelets">
<h:head>
<h:outputStylesheet library="css" name="employee.css"  />

    <title>Welcome</title>
</h:head>
<h:body>
<h:form>
    <h:panelGrid>
        <h:outputLabel for="first_name" value="First Name" />
        <h:inputText id="first_name"
            value="#{employeeController.model.firstName}" required="true" />

        <h:outputLabel for="last_name" value="Last Name" />
        <h:inputText id="last_name"
            value="#{employeeController.model.lastName}" required="true" />

        <h:outputLabel for="email" value="Email" />
        <h:inputText id="email" value="#{employeeController.model.email}"
            required="true" />

        <h:outputLabel for="phone_number" value="Phone" />
        <h:inputText id="phone_number"
            value="#{employeeController.model.phoneNumber}" required="true" />

        <h:outputLabel for="salaray" value="Salary" />
        <h:inputText id="salaray" value="#{employeeController.model.salary}"
            required="true" />

        <h:commandButton value="Save(AJAX)">
            <f:ajax execute="@form" render="modelOutput"/>
        </h:commandButton>
    </h:panelGrid>
</h:form>
    <h:panelGrid id="modelOutput">
        <h:outputText id="fName"
            value="First Name: #{employeeController.model.firstName}" />
        <h:outputText value="Last Name: #{employeeController.model.lastName}" />
        <h:outputText value="E-Mail: #{employeeController.model.email}" />
        <h:outputText
            value="Phonenumber: #{employeeController.model.phoneNumber}" />
        <h:outputText value="Salary: #{employeeController.model.salary}" />
    </h:panelGrid>
</h:body>
</html>

The model is a simple javabean with just getter and setter for the values.

When I click on the ajax Button I can see a request going to the server, holding my Form-Data. I get a partial-response back with the hard-coded values for the outputtexts - but not with the model values. I can see that also my setters are not called.

If I change the ajax execute to a specific inputText component id - than it works for that one.

What am I missing?

-- EmployeeBean

public class EmployeeBean {

    private String firstName;
    private String lastName;

    private String email;
    private String phoneNumber;
    private Float salary;

    public String getFirstName() {
        return firstName;
    }

    public void setFirstName(String firstName) {
        this.firstName = firstName;
    }

    public String getLastName() {
        return lastName;
    }

    public void setLastName(String lastName) {
        this.lastName = lastName;
    }

    public String getEmail() {
        return email;
    }

    public void setEmail(String email) {
        this.email = email;
    }

    public String getPhoneNumber() {
        return phoneNumber;
    }

    public void setPhoneNumber(String phoneNumber) {
        this.phoneNumber = phoneNumber;
    }

    public Float getSalary() {
        return salary;
    }

    public void setSalary(Float salary) {
        this.salary = salary;
    }



}

EmployeeController:

@ManagedBean
@RequestScoped
public class EmployeeController {

    private EmployeeBean model;

    @PostConstruct
    public void init() {
        model = new EmployeeBean();
    }

    public EmployeeBean getModel() {
        return model;
    }

    public void clearAjax(AjaxBehaviorEvent ajaxBehaviorEvent) {
        clearUiAreaForm(ajaxBehaviorEvent.getComponent());
        System.out.println("Form cleared");
    }
}

Solution

  • So I found the reason and the solution.

    The main problem is following:

    From a lifecycle listener I saw that the processing on server side skipped phases 4 & 5. What is the reason for this? Well - phase 3 is about validation. My form had some validation restrictions, i.e. required.

    I did not fill out all the input fields and therefor validation phase was not successfull. JSF then skips Phase 4 & 5 - now we also know why the setters were not called.

    Here comes the kicker: It fails completely silently! You do get a good ajax request containing the form parameters. And you do get a good partial-response - but without dynamic values from the bean.

    There was no error message in the application log (well, there should also be none but this leads you to knowing there was no exception). But apparently Ajax does not fill any component like it is done when you use a full page request. You do not have any information about the failing validation. If you do not use a lifecycle information output than you can't see it in any way.

    Additionally, the only thing I was able to find out is adding a component that renders some information. Add a hardcoded outputText that will only be rendered on the information of facesContext.validationFailed and render it by ajax facelet.

    <h:commandButton value="Save (AJAX)">
                    <f:ajax execute="@form" render="modelOutput global_flag_validation_failed_render" />
                </h:commandButton>
                <h:messages />
                <h:panelGroup id="global_flag_validation_failed_render">
                    <h:outputText id="global_flag_validation_failed" value="Fehler in Ajax aufgetreten!"
                        rendered="#{facesContext.validationFailed}" />
                </h:panelGroup>
    

    I have no idea if this is the specified and wanted behaviour of Mojarra 2.2.14 - does anybody know it?