- 

Available since OmniFaces 1.0

The ResetInputAjaxActionListener will reset input fields which are not executed during ajax submit, but which are rendered/updated during ajax response. This will prevent those input fields to remain in an invalidated state because of a validation failure during a previous request. This is very useful for cases where you need to update one form from another form by for example a modal dialog, or when you need a cancel/clear button.

How does it work? First, here are some Faces facts:

  • When Faces validation succeeds for a particular input component during the validations phase, then the submitted value is set to null and the validated value is set as local value of the input component.
  • When Faces validation fails for a particular input component during the validations phase, then the submitted value is kept in the input component.
  • When at least one input component is invalid after the validations phase, then Faces will not update the model values for any of the input components. Faces will directly proceed to render response phase.
  • When Faces renders input components, then it will first test if the submitted value is not null and then display it, else if the local value is not null and then display it, else it will display the model value.
  • As long as you're interacting with the same Faces view, you're dealing with the same component state.

So, when the validation has failed for a particular form submit and you happen to need to update the values of input fields by a different ajax action or even a different ajax form (e.g. populating a field depending on a dropdown selection or the result of some modal dialog form, etc), then you basically need to reset the target input components in order to get Faces to display the model value which was edited during invoke action. Otherwise Faces will still display its local value as it was during the validation failure and keep them in an invalidated state.

The ResetInputAjaxActionListener is designed to solve exactly this problem. There are basically three ways to configure and use it:

  • Register it as <phase-listener> in faces-config.xml. It'll be applied to every single ajax action throughout the webapp, on both UIInput and UICommand components.

    <lifecycle>
        <phase-listener>org.omnifaces.eventlistener.ResetInputAjaxActionListener</phase-listener>
    </lifecycle>
  • Or register it as <action-listener> in faces-config.xml. It'll only be applied to ajax actions which are invoked by an UICommand component such as <h:commandButton> and <h:commandLink>.

    <application>
        <action-listener>org.omnifaces.eventlistener.ResetInputAjaxActionListener</action-listener>
    </application>
  • Or register it as <f:actionListener> on the invidivual UICommand components where this action listener is absolutely necessary to solve the concrete problem. Note that it isn't possible to register it on the individual UIInput components using the standard Faces tags.

    <h:commandButton value="Update" action="#{bean.updateOtherInputs}">
        <f:ajax execute="currentInputs" render="otherInputs" />
        <f:actionListener type="org.omnifaces.eventlistener.ResetInputAjaxActionListener" />
    </h:commandButton>

This works with standard Faces, PrimeFaces and RichFaces actions. Only for RichFaces there's a reflection hack, because its ExtendedPartialViewContextImpl always returns an empty collection for render IDs. See also RF issue 11112.

Design notice: being a phase listener was mandatory in order to be able to hook on every single ajax action as standard Faces API does not (seem to?) offer any ways to register some kind of AjaxBehaviorListener in an application wide basis, let alone on a per <f:ajax> tag basis, so that it also get applied to ajax actions in UIInput components. There are ways with help of SystemEventListener, but it ended up to be too clumsy.

See also:
Faces spec issue 1060

PrimeFaces

Note that PrimeFaces has implemented the same idea later in its new version 3.4 <p:resetInput>, so if you happen to use PrimeFaces already and want to apply it on specific command buttons only, then you may want to use it instead.

Demo

Form without ResetInputAjaxActionListener

input1: Enter something (but don't enter "Updated!").

input2: Leave this field empty or enter a non-numeric value to cause a validation failure.

Press "submit" and then "update". The "update" simulates changing model values externally.

Problem: input1 is not updated with text "Updated!". And when MyFaces is used, input2 isn't updated with text "42" either.


Same form, with ResetInputAjaxActionListener on "update"

input1: Enter something (but don't enter "Updated!").

input2: Leave this field empty or enter a non-numeric value to cause a validation failure.

Press "submit" and then "update". The "update" simulates changing model values externally.

Solved: input1 is now updated with text "Updated!".

Demo source code
<h3>Form without <code>ResetInputAjaxActionListener</code></h3>
<h:form id="form1">
    <p>input1: Enter something (but don't enter "Updated!").</p>
    <p>
        <h:inputText id="input1" value="#{resetInputBean.input1}" required="true" />
        <h:message for="input1" />
    </p>
    <p>input2: Leave this field empty or enter a non-numeric value to cause a validation failure.</p>
    <p>
        <h:inputText id="input2" value="#{resetInputBean.input2}" required="true" />
        <h:message for="input2" />
    </p>
    <p>Press "submit" and then "update". The "update" simulates changing model values externally.</p>
    <p>
        <h:commandButton value="Submit">
            <f:ajax execute="@form" render="@form" />
        </h:commandButton>
        <h:commandButton value="Update" action="#{resetInputBean.update}" >
            <f:ajax execute="@this" render="@form" />
        </h:commandButton>
        <h:outputText value="OK!" rendered="#{facesContext.postback and not facesContext.validationFailed}" />
    </p>
    <p><b>Problem</b>: input1 is not updated with text "Updated!". And when MyFaces is used, input2 isn't updated with text "42" either.</p>
</h:form>

<hr />

<h3>Same form, with <code>ResetInputAjaxActionListener</code> on "update"</h3>
<h:form id="form2">
    <p>input1: Enter something (but don't enter "Updated!").</p>
    <p>
        <h:inputText id="input1" value="#{resetInputBean.input1}" required="true" />
        <h:message for="input1" />
    </p>
    <p>input2: Leave this field empty or enter a non-numeric value to cause a validation failure.</p>
    <p>
        <h:inputText id="input2" value="#{resetInputBean.input2}" required="true" />
        <h:message for="input2" />
    </p>
    <p>Press "submit" and then "update". The "update" simulates changing model values externally.</p>
    <p>
        <h:commandButton value="Submit">
            <f:ajax execute="@form" render="@form" />
        </h:commandButton>
        <h:commandButton value="Update" action="#{resetInputBean.update}" >
            <f:ajax execute="@this" render="@form" />
            <f:actionListener type="org.omnifaces.eventlistener.ResetInputAjaxActionListener" />
        </h:commandButton>
        <h:outputText value="OK!" rendered="#{facesContext.postback and not facesContext.validationFailed}" />
    </p>
    <p><b>Solved</b>: input1 is now updated with text "Updated!".</p>
</h:form>