在Core JavaServer Faces中,我们发现了以下从数据表中删除行的例子,其中tableData.names是一个Name对象列表:
<h:dataTable value="#{tableData.names}" var="name" ... />
<h:commandLink value="Delete" action="#{tableData.deleteRow(name)}" />
</h:dataTable>
它带有一个警告,如果我们使用请求范围,并且列表"在表的呈现和响应的解码之间"发生变化,则可能不会删除正确的行。
注意:如果数据表的值具有请求作用域,请确保在表的呈现和响应的解码之间数据不会改变(第3版第226页)
有人能用JSF生命周期来解释这句话吗?如果"呈现响应"是JSF生命周期的最后阶段,那么"解码响应"应该放在哪里呢?它们是否意味着对以下回发请求的解码(它发送一个生成的ID,通过该ID来标识行,从而标识name对象)?
以及:我们如何以更健壮的方式实现这一点?
方法表达式不是在显示表单的请求(书中讨论的编码步骤)期间求值,而是在处理表单提交的请求(书中讨论的解码步骤)期间求值。数据表行的输入和操作是根据表的行索引确定的。在表单提交的处理过程中,JSF重新遍历数据模型,以便找到提交的值和调用的操作。
因此,如果<h:dataTable value>
绑定到一个请求作用域bean,因此数据模型在每个请求的基础上重新初始化,那么在处理表单提交期间,您将冒着#{item}
实际上在错误的索引上引用项目的风险,因为在显示表单和提交表单的请求之间,DB可能已经检索了一个新项目,或者已经删除了另一个项目,这可能会将所需的项目移动到不同的索引。
为了避免这种情况,需要将bean放在视图作用域中,以便在多个回发中保留在初始请求的(post)构造函数中初始化的完全相同的数据模型,而不需要在每个请求开始时重新初始化它,从而可能包含不同的项或以不同的顺序。如果数据模型的加载与最终用户完全控制的特定请求参数(如搜索查询)相关联,则影响更大。
另一种选择是使用GET链接,这在这个特殊的非幂等的"删除这个项目"的情况下实际上是一个坏的选择,但在幂等的"编辑这个项目"的情况下是一个好的选择。所需的项立即呈现为<a>
元素的查询字符串参数。
<h:link value="Edit" outcome="edit">
<f:param name="id" value="#{item.id}" />
</h:link>