p:ajax immediate="true" on UIInput 不跳过验证
p:ajax immediate="true" on UIInput does not skip validation
TL;DR
<h:form>
<p:inputText required="true">
<p:ajax event="click" immediate="true" process="@this" update="@this" />
</p:inputText>
</h:form>
点击会导致验证,即使 ajax 是立即的。为什么?
我做不出来终于完成了:
<h:form>
<p:dataGrid var="elem" value="#{immediateTestBean.elements}" columns="3">
<f:facet name="footer">
<p:commandButton action="#{immediateTestBean.newElement}" immediate="true"
process="@namingcontainer" update="@namingcontainer" value="#{bundle.add}" />
</f:facet>
<h:panelGrid columns="3">
<p:selectOneMenu value="#{elem.type}">
<p:ajax immediate="true" process="@form" update="@form" />
<f:selectItem itemValue="text" itemLabel="#{bundle.text}" />
<f:selectItem itemValue="date" itemLabel="#{bundle.date}" />
</p:selectOneMenu>
<h:panelGroup id="detail">
<p:inputText value="#{elem.text}" required="true" rendered="#{elem.type == 'text'}"
validator="#{immediateTestBean.testValidate}" label="#{bundle.text}">
<f:validateLength minimum="4" />
</p:inputText>
<p:calendar value="#{elem.date}" required="true" rendered="#{elem.type == 'date'}"
pattern="dd/MM/yyyy" readonlyInput="true" showOn="button"
validator="#{immediateTestBean.testValidate}" label="#{bundle.date}" />
</h:panelGroup>
<p:commandButton action="#{immediateTestBean.removeElement(elem)}" immediate="true"
process="@namingcontainer" update="@namingcontainer" value="#{bundle.remove}" />
</h:panelGrid>
</p:dataGrid>
<p:spacer height="10" style="display: block" />
<p:commandButton process="@form" update="@form" value="#{bundle.submit}" />
</h:form>
一切正常,除了 <p:ajax immediate="true" process="@form" update="@form" />
不像 UICommand
那样:验证发生(但在 APPLY_REQUEST_VALUES
阶段)。
我试图深入了解 JSF 源代码并注意到 UICommand
s 默认调用 facesContext.renderResponse()
ActionListenerImpl.processAction()
,所以我尝试使用:
<p:ajax listener="#{facesContext.renderResponse}" immediate="true" process="@form" update="@form" />
现在,当我 select 'date' 时,验证被跳过,但 <h:panelGroup id="detail">
没有更新。
然后,我认为跳过验证也会导致更新模型跳过,所以我调用:
<p:ajax listener="#{immediateTestBean.skip}" immediate="true" process="@form" update="@form" />
终于成功了。
但是出现了两个问题:
- 为什么这么复杂?
- 是否有更简单的方法来实现相同的行为?
但是,这里是 bean 的代码:
@ManagedBean
@ViewScoped
public class ImmediateTestBean implements Serializable
{
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(ImmediateTestBean.class);
public class Element
{
private String type = "text";
private String text;
private Date date;
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
public String getText()
{
return text;
}
public void setText(String text)
{
this.text = text;
}
public Date getDate()
{
return date;
}
public void setDate(Date date)
{
this.date = date;
}
}
private final List<Element> elements = new ArrayList<>();
public void testValidate(FacesContext context, UIComponent component, Object value)
{
logger.debug("phase: {}", context.getCurrentPhaseId());
}
public void skip()
{
logger.debug("Faces.getCurrentPhaseId(): {}", Faces.getCurrentPhaseId());
UIInput component = (UIInput) Components.getCurrentComponent();
logger.debug("component: {}", component);
FacesContext context = Faces.getContext();
component.validate(context);
if(component.isValid())
{
component.updateModel(context);
}
Faces.renderResponse();
}
public void newElement()
{
elements.add(new Element());
}
public void removeElement(Element elem)
{
elements.remove(elem);
}
public List<Element> getElements()
{
return elements;
}
}
- 为什么这么复杂?
根据 PrimeFaces 文档:“此功能简单但功能强大,足以进行 组验证,避免验证不需要的
组件”。
您可以在 PrimeFaces 文档的 4.2 部分处理
章中找到部分处理示例
尝试使用<p:ajax immediate="true" process="@this, #{p:component('detail')}" update="#{p:component('detail')}" />
immediate="true"
将跳过流程验证、更新模型值和调用应用程序 JSF 生命周期阶段,但 process="@this, #{p:component('detail')}"
将仅执行应用请求值、流程验证、更新模型值和调用应用程序 JSF 生命周期阶段组件 <h:panelGroup id="detail"
和 <p:selectOneMenu value="#{elem.type}">...
这是JSF的不足,无法用immediate="true"
在所有情况下模拟。
TL;DR
<h:form>
<p:inputText required="true">
<p:ajax event="click" immediate="true" process="@this" update="@this" />
</p:inputText>
</h:form>
点击会导致验证,即使 ajax 是立即的。为什么?
我做不出来终于完成了:
<h:form>
<p:dataGrid var="elem" value="#{immediateTestBean.elements}" columns="3">
<f:facet name="footer">
<p:commandButton action="#{immediateTestBean.newElement}" immediate="true"
process="@namingcontainer" update="@namingcontainer" value="#{bundle.add}" />
</f:facet>
<h:panelGrid columns="3">
<p:selectOneMenu value="#{elem.type}">
<p:ajax immediate="true" process="@form" update="@form" />
<f:selectItem itemValue="text" itemLabel="#{bundle.text}" />
<f:selectItem itemValue="date" itemLabel="#{bundle.date}" />
</p:selectOneMenu>
<h:panelGroup id="detail">
<p:inputText value="#{elem.text}" required="true" rendered="#{elem.type == 'text'}"
validator="#{immediateTestBean.testValidate}" label="#{bundle.text}">
<f:validateLength minimum="4" />
</p:inputText>
<p:calendar value="#{elem.date}" required="true" rendered="#{elem.type == 'date'}"
pattern="dd/MM/yyyy" readonlyInput="true" showOn="button"
validator="#{immediateTestBean.testValidate}" label="#{bundle.date}" />
</h:panelGroup>
<p:commandButton action="#{immediateTestBean.removeElement(elem)}" immediate="true"
process="@namingcontainer" update="@namingcontainer" value="#{bundle.remove}" />
</h:panelGrid>
</p:dataGrid>
<p:spacer height="10" style="display: block" />
<p:commandButton process="@form" update="@form" value="#{bundle.submit}" />
</h:form>
一切正常,除了 <p:ajax immediate="true" process="@form" update="@form" />
不像 UICommand
那样:验证发生(但在 APPLY_REQUEST_VALUES
阶段)。
我试图深入了解 JSF 源代码并注意到 UICommand
s 默认调用 facesContext.renderResponse()
ActionListenerImpl.processAction()
,所以我尝试使用:
<p:ajax listener="#{facesContext.renderResponse}" immediate="true" process="@form" update="@form" />
现在,当我 select 'date' 时,验证被跳过,但 <h:panelGroup id="detail">
没有更新。
然后,我认为跳过验证也会导致更新模型跳过,所以我调用:
<p:ajax listener="#{immediateTestBean.skip}" immediate="true" process="@form" update="@form" />
终于成功了。
但是出现了两个问题:
- 为什么这么复杂?
- 是否有更简单的方法来实现相同的行为?
但是,这里是 bean 的代码:
@ManagedBean
@ViewScoped
public class ImmediateTestBean implements Serializable
{
private static final long serialVersionUID = 1L;
private static final Logger logger = LoggerFactory.getLogger(ImmediateTestBean.class);
public class Element
{
private String type = "text";
private String text;
private Date date;
public String getType()
{
return type;
}
public void setType(String type)
{
this.type = type;
}
public String getText()
{
return text;
}
public void setText(String text)
{
this.text = text;
}
public Date getDate()
{
return date;
}
public void setDate(Date date)
{
this.date = date;
}
}
private final List<Element> elements = new ArrayList<>();
public void testValidate(FacesContext context, UIComponent component, Object value)
{
logger.debug("phase: {}", context.getCurrentPhaseId());
}
public void skip()
{
logger.debug("Faces.getCurrentPhaseId(): {}", Faces.getCurrentPhaseId());
UIInput component = (UIInput) Components.getCurrentComponent();
logger.debug("component: {}", component);
FacesContext context = Faces.getContext();
component.validate(context);
if(component.isValid())
{
component.updateModel(context);
}
Faces.renderResponse();
}
public void newElement()
{
elements.add(new Element());
}
public void removeElement(Element elem)
{
elements.remove(elem);
}
public List<Element> getElements()
{
return elements;
}
}
- 为什么这么复杂?
根据 PrimeFaces 文档:“此功能简单但功能强大,足以进行 组验证,避免验证不需要的 组件”。
您可以在 PrimeFaces 文档的 4.2 部分处理
章中找到部分处理示例尝试使用<p:ajax immediate="true" process="@this, #{p:component('detail')}" update="#{p:component('detail')}" />
immediate="true"
将跳过流程验证、更新模型值和调用应用程序 JSF 生命周期阶段,但 process="@this, #{p:component('detail')}"
将仅执行应用请求值、流程验证、更新模型值和调用应用程序 JSF 生命周期阶段组件 <h:panelGroup id="detail"
和 <p:selectOneMenu value="#{elem.type}">...
这是JSF的不足,无法用immediate="true"
在所有情况下模拟。