尝试理解 immediate="true" 的含义,为什么它会在不应该跳过输入时跳过输入。

27

刚刚我以为我已经理解了立即执行函数... *叹气*

考虑下面的JSF页面:

<h:inputText value="#{testBean.text}" required="true" />
<h:commandButton actionListener="#{testBean.doFoo}" value="Do Foo" />
<h:commandButton immediate="true" actionListener="#{testBean.doBar}" value="Do Bar" /><br />
<h:outputText value="#{testBean.didSomething}" />

并且这是一个后端Bean:

public class TestBean {
   private String didSomething = "Nothing done yet";
   // + getter

public void doFoo() {
    didSomething = "Did foo!";        
}

public void doBar() {
    didSomething = "Did bar!";        
}

根据我所阅读的有关immediate的所有内容,我期望以下结果:

  • 当尝试执行foo操作时,而没有为输入字段提供值时,在processValidationsPhase期间会发生错误,因此该操作不会被执行,直接在该阶段之后使用错误消息重新呈现页面。变量didSomething的值保持不变。(这符合预期)

  • 当尝试执行bar操作时,而没有为输入字段提供值时,由于设置了immediate属性,该操作将在applyRequestValuesPhase期间执行。变量didSomething会被更改。(这符合预期)

关于接下来会发生什么,这个描述指出:

"空返回值(作为操作方法的结果)会使处理继续进行,即非立即组件将得到验证,然后更新模型(如果没有发生验证错误)。对于返回void的操作监听器方法,有必要调用facesContext.renderResponse();如果不需要正常流程,则必须调用facesContext.renderResponse()。"

基于此,我认为处理会像正常情况一样继续进行(因为我的操作方法既没有返回结果也没有强制使用renderResponse()),导致相同的验证错误。唯一的区别是它发生在设置didSomething之后。然而,事实并非如此。相反,感觉网站仍会跳过所有剩余阶段,输入字段不受影响。重新呈现页面时不会显示任何错误消息。

有人能否解释一下我的理解出了什么问题?


还有哪些阶段?除了调用操作阶段之后,只剩下一个阶段:呈现响应阶段。除了呈现响应之外,您到底希望发生什么?相关链接:http://balusc.blogspot.com/2006/09/debug-jsf-lifecycle.html - BalusC
那么,立即按钮的操作不是在InvokeActions期间调用的,而是在ApplyRequestValues结束时调用的,对吧?因此,如果它不干扰正常流程,仍然应该有ProcessValidations、UpdateModel和InvokeActions吗? - Louise
我明白你的意思了。我已经发布了一个答案。 - BalusC
1个回答

46

对于具有 immediate="true" 属性的按钮,操作确实会在应用请求值阶段调用,并且所有剩余阶段都将被跳过。这也是此属性的唯一目的:在应用请求值阶段立即处理(解码、验证、更新和调用)组件。

所有未设置 immediate="true" 的输入都将被忽略。只有设置了 immediate="true" 的输入才会被处理,但这也发生在应用请求值阶段。如果一切都已经在应用请求值阶段发生了,为什么还要调用剩余的阶段呢?

Debug JSF lifecycle文章中,您可以找到以下总结,它应该说明何时(不)使用 immediate="true"

好的,何时应该使用 immediate 属性?

如果还不够清楚,请看下面的总结,包括真实世界的用例,以及何时可能有益:

  • 仅在 UIInput 中设置时,验证过程将在应用请求值阶段而不是处理验证阶段进行。使用此功能为相关的 UIInput 组件优先进行验证。如果其中任何一个组件的验证/转换失败,非立即处理的组件将不会被验证/转换。

  • 仅在 UICommand 中设置时,将跳过应用请求值阶段到更新模型值阶段中的任何 UIInput 组件。使用此功能可跳过整个表单的处理。例如,“取消”或“返回”按钮。

  • 如果在UIInputUICommand组件中都设置了该属性,那么对于没有设置该属性的任何UIInput组件,在应用请求值阶段到更新模型值阶段之间将被跳过。使用这个属性可以跳过整个表单的处理,只处理某些字段(使用immediate)。例如,在登录表单中,有一个需要但不是即时的密码字段时,可以用“忘记密码”按钮来跳过整个表单的处理。

另请参阅:


我在问题中更新了来自链接来源的引用。如果我错了,请纠正我,但是他们的描述和你的描述给出了不同的图片 - 你的描述与实际行为相匹配。他们的描述有点错误吗? - Louise
1
显然,MyFaces中存在一个错误,这个文章的作者却认为它是正确的行为。 - BalusC
jsf 2.2规范的2.5.1节非常易读,也支持BalusC的精彩解释。 - djeikyb
我认为最后一个项目中给出的例子,即“登录表单中的“忘记密码”按钮,带有必填但非立即密码字段”,更适合第二个项目。当按下该按钮时,表单中不应验证任何内容。(在实际应用程序中,通常会使用链接而不是按钮,以直接导航到密码恢复页面)。 - Tiny
3
@Tiny:带有immediate="true"属性的“忘记密码”按钮实际上处理了用户名(因为它有immediate="true"属性),但没有处理密码(因为它没有immediate="true"属性)。它基本上重用登录表单作为密码重置表单。如果使用GET链接/按钮,您需要按两次按钮(一次进入密码重置表单,另一次提交表单),并且如果您未将用户名作为请求参数传递,则可能需要重新输入用户名。 - BalusC
我花了一些时间才理解,但最终我成功地彻底理解了它。谢谢。 - Tiny

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接