-
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 notnull
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>
infaces-config.xml
. It'll be applied to every single ajax action throughout the webapp, on bothUIInput
andUICommand
components.<lifecycle> <phase-listener>org.omnifaces.eventlistener.ResetInputAjaxActionListener</phase-listener> </lifecycle>
-
Or register it as
<action-listener>
infaces-config.xml
. It'll only be applied to ajax actions which are invoked by anUICommand
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 invidivualUICommand
components where this action listener is absolutely necessary to solve the concrete problem. Note that it isn't possible to register it on the individualUIInput
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.
Form without ResetInputAjaxActionListener
Same form, with ResetInputAjaxActionListener
on "update"
<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>
package org.omnifaces.showcase.eventlisteners;
import java.io.Serializable;
import jakarta.inject.Named;
import org.omnifaces.cdi.ViewScoped;
@Named
@ViewScoped
public class ResetInputBean implements Serializable {
private static final long serialVersionUID = 1L;
private String input1;
private Integer input2;
public void update() {
input1 = "Updated!";
input2 = 42;
}
public String getInput1() {
return input1;
}
public void setInput1(String input1) {
this.input1 = input1;
}
public Integer getInput2() {
return input2;
}
public void setInput2(Integer input2) {
this.input2 = input2;
}
}