我有一个JSF页面,其中包含一些常见元素,然后是基于各种用户操作动态加载和包含的4个部分。我有封装各种include功能的bean,其结构如下:
mainview.xhtml
--backed by--> mainViewBean (request scope)
--with managed property--> mainViewView (view scope)
include1.xhtml
--backed by--> include1Bean (request scope)
--with managed property--> mainViewBean
include2.xhtml
--backed by--> include2Bean (request scope)
--with managed property--> mainViewBean
include3.xhtml
--backed by (and with component bindings to)--> include3Bean (request scope)
--with managed property--> mainViewBean
include4.xhtml
--backed by--> mainViewBean
在主视图页面上,每个include都通过一系列有条件渲染的h:panelGroups包含,如BalusC在以下问题中所述:
https://stackoverflow.com/a/9897016/945403
https://stackoverflow.com/a/7113961/945403
视图范围的bean包含一些关于用户活动的当前状态、他们当前正在查看的项目等的各种视图信息
每个包含的视图都通过ajax和非ajax帖子执行各种任务。我的问题是,某些小组中的某些行为似乎会导致视图范围被破坏,而且似乎没有韵律或理由。我提前为下面概述的复杂工作流程道歉,但我会尽可能清楚。
如果我在include3.html中执行ajax操作,它们只在include中呈现组件,我可以整天都这样做,并且视图范围仍然存在。如果我执行非ajax帖子,刷新将更新(并可能添加以前未渲染的)include4.html。然后我可以在任何其他include中执行操作。
如果我在include1.html中执行ajax操作,它只调用自己的backingbean中的方法,并且只更新自己的组件,那么视图范围仍然存在。如果我执行一个ajax操作,该操作调用include3Bean中的一个方法,并更新包含include2.xhtml和include3.xhtml的div,则视图作用域将保留,只要我继续在include1.xhtml中执行操作,视图作用域就会保留。一旦我尝试在另一个include中执行另一个操作,视图范围就会被破坏。
在这一点上,我心想,问题一定在于我更新的内容与调用的操作不同。但这似乎并不是我现在要解释的问题(或者至少是唯一的问题)。
include2.xhtml有一个ajax操作,当它被激发时,它会调用mainViewBacking中的一个方法,并更新包含include2.xhtml和include3.xhtml的div。如果我继续执行这个操作,或者在include2.x.html中采取任何其他操作(包括非ajax操作),一切都会按预期进行,并且视图范围仍然存在。但是,如果我随后在include3.html中执行操作,则视图范围将被破坏。奇怪的是,我可以在include1.html中执行操作,只要我愿意,他们就会继续访问视图范围,但如果我试图返回并在include2.html或include3.html中执行一个操作,视图范围就会再次丢失。
在这一点上,我有点不知所措,甚至不知道该如何找出问题所在。我假设刷新(以及删除或添加)includes会导致视图范围丢失,但从之前链接的问题来看,这似乎不是问题,因为ui:includes的实际src值不是动态生成的。我确实关闭了mainview.xhtml的部分状态保存。
这些动态显示的ui:includes是否会破坏视图范围?
编辑2014-02-28:
在查看最新的omnifaces展示时,我注意到BalusC已经实现了一个脚本,该脚本应该可以解决同样的问题,而且可能比下面的代码更好。
http://showcase.omnifaces.org/scripts/FixViewState
原始答案:
事实证明,这是JSF中的一个错误,正如BalusC在这里描述的那样:http://balusc.blogspot.com/2011/09/communication-in-jsf-20.html#AjaxRenderingOfContentWhichContainsAnotherForm
不幸的是,由于某种原因,他的解决方案对我来说并不总是有效的。因此,我编写了自己的(稍微更烦人的)解决方案,如果其他人需要,我会在这里提供给他们
我在mainview.xhtml页面中添加了以下javascript:
var lastForm = null;
function getViewStateFromLastForm(callingElement){
var currentForm = $(callingElement).closest("form");
var formId = currentForm.attr("id");
if(lastForm != null ){
var viewState = $("#" + lastForm).children("[name='javax.faces.ViewState']").val();
currentForm.children("[name='javax.faces.ViewState']").remove();
$('<input/>').attr({
type: 'hidden',
id: 'javax.faces.ViewState',
name: 'javax.faces.ViewState',
autocomplete: 'off',
value: viewState
}).appendTo(currentForm);
}
lastForm = formId;
}
然后,提交给服务器的各个包含页面上的每个按钮的onclick属性中都有一个调用getViewStateFromLastForm(this);
。