ADF的UncommittedDataWarning机能使用两个标志来决定当前页面是否是编辑过的(即为脏的):
_hasLocalUncommitted用来标示客户端是否为脏状态,
_dataDirty是用来标示服务端是否为脏的状态。
1. 当用户在页面上编辑一个非autoSubmit的控件时,ADF框架会将_hasLocalUncommitted设置为true,因此当用户离开离开页面时,就会弹出下面的提示窗口。
当用户试图关闭页面时,就会弹出下面的提示窗口。
2. 当用户在页面上编辑一个autoSubmit的控件时,ADF会提交一个PPR请求,请求的Response会包含下面的内容:
<script>AdfPage.PAGE.setDataDirty(false);</script>
<script>AdfPage.PAGE.setProcessedRoots("d2");</script>
其中的第一个javascript脚本会将_dataDirty设置为false,表明服务端不是脏的状态;第二个javascript脚本会将_hasLocalUncommitted设置为false,表明客户端不是脏的状态(前提是页面上无验证错误,当页面上有验证错误时,此处的脚本不会重置客户端的状态,因此客户端仍然为脏的状态)。所以当用户试图离开或者关闭页面时,就不会提示上面的提示窗口。
3. 当服务端的状态为脏时(当应用程序在请求页面时,修改了页面上绑定的数据源的值的时候,比如页面A和B使用了同样的VO作为Model层绑定到了Page Definition文件中,当从页面A跳转到页面B的时候,在页面A的按的ActionListener当中修改了VO的行记录),那么在请求的页面的Response中会包含下面的内容:
<script type="text/javascript">
AdfBootstrap._extendedScripts=[function({AdfPage.PAGE.setDataDirty(true);}];
</script>
上面的javascript脚本会设置_dataDirty设置为true,表明服务端是脏的状态,因此当用户试图离开或者关闭页面时,就是提示上面的提示消息。
4. 当页面上有验证错误时,若用户操作了非autoSubmit的控件,那么客户端仍然保持脏的状态,若用户操了autoSubmit的控件,但是因为页面有验证错误,客户端的状态不会被重置,所以仍然为脏。因此当用户试图离开或者关闭页面时,就是提示上面的提示消息。
实际情况是,当用户在页面上进行了编辑操作后,无论他操作的是否为autoSubmit的控件,当他试图离开或者关闭页面时,都应该提示消息给用户。但是ADF提供的功能在用户操作autoSubmit的控件时,不起作用。那么该如何实现上述需求呢?
思路1:
1.1. 添加一个dirty filed在页面上;
1.2. 给页面上的所有autoSubmit控件添加一个valueChange listener用来标示dirty filed;
1.3. 给页面的definition文件文件设置一个状态监听器(controllerClass=PPRHookListener),当PPR请求完毕后,添加一段javascript脚本,用来检查dirty field。当dirty field不为空时,调用AdfPage.PAGE.setDataDirty方法,将当前页面设置为脏状态。
1.3步的java代码如下:
public class PPRHookListener implements PhaseListener {
public void afterPhase(PagePhaseEvent pagePhaseEvent) {
FacesPageLifecycleContext ctx =
(FacesPageLifecycleContext)pagePhaseEvent.getLifecycleContext();
if (pagePhaseEvent.getPhaseId() != Lifecycle.PREPARE_RENDER_ID) {
return;
}
if(AdfFacesContext.getCurrentInstance().isPostback() )
FacesContext context = FacesContext.getCurrentInstance();
ExtendedRenderKitService service =
(ExtendedRenderKitService)Service.getRenderKitService
(context, ExtendedRenderKitService.class);
service.addScript(context, " markDirtyIfNeeded('dirtyFiledId');");
}
}
}
但是因为ADF框架会在上面添加的javascript之后,添加下面的重置状态的javascript脚本,导致上面添加的javascript又被覆盖了,因此此思路行不通。
<script>markDirtyIfNeeded('dirtyFiledId');</script>
<script>AdfPage.PAGE.setDataDirty(false);</script>
<script>AdfPage.PAGE.setProcessedRoots("d2");</script>
思路2:
1.1. 添加一个dirty filed在页面上;
1.2. 给页面上的所有autoSubmit控件添加一个valueChange listener用来标示dirty filed;
1.3. 重写当前页面的setDataDirty方法,检查dirty filed是否为空,当不为空时,则将当前页面设置为脏状态。
涉及到的代码如下:
// Use this method as page's load listener.
function overrideSetDataDirtyForPage(dirtyFieldId) {
AdfPage.PAGE.setDataDirty = function(value) {
AdfDhtmlPage.prototype.setDataDirty(value);
var txtDirty = AdfPage.PAGE.findComponentByAbsoluteId(dirtyFieldId);
if (txtDirty == null) {
return;
}
var dirtyFlag = txtDirty.getValue();
if (dirtyFlag == null || dirtyFlag == '') {
return;
}
AdfDhtmlPage.prototype.setDataDirty(true);
}
}
//Use this method as control's value change listener
function markDirty(dirtyFieldId) {
return function(evt) {
var txtDirty = AdfPage.PAGE.findComponentByAbsoluteId(dirtyFieldId);
if (txtDirty != null) {
txtDirty.setValue("dirty");
}
}
}
页面使用的代码如下:
<!-- Register load listener for the page -->
<af:clientListener method= overrideSetDataDirtyForPage('txtLocalDirtyFlg')" type="load"/>
<!-- Register value change listener for the autoSubmittted control-->
<af:selectOneChoice label="List"
id="soc1"
autoSubmit="true"
....>
<f:selectItems value="#{pageFlowScope.list}" id="si3"/>
<af:clientListener type="valueChange" method="markDirty('txtLocalDirtyFlg')"/>
</af:selectOneChoice>
此思路有效的避免了ADF框架的默认行为,可以解决上述问题。