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框架的默认行为,可以解决上述问题。