UIShell with Dynamic Tabs: Marking the Current Tab Dirty

THIS POST IS OBSOLETE, THE TECHNIQUE DESCRIBED BELOW HAS SOME ISSUES. A NEW AND BETTER IMPLEMENTATION IS DISCUSSED HERE:  http://blogs.oracle.com/jheadstart/entry/core_adf11_uishell_with_dynamic

The Dynamic Tabs UI Shell Template includes an API to mark the current tab as dirty or clean.
By marking a tab as dirty, the tab label will appear in italics, and when closing the tab, the end user will get an alert that pending changes will be lost. Nice functionality, but it is left to the ADF developer to write custom code to call this API.

In the upcoming 11.1.1.3 release of JHeadstart, we added support to automatically call this API by checking the data control state of the current tab. This is done using a custom phase listener, that contains the following code:

package oracle.jheadstart.controller.jsf.listener;

import javax.faces.event.PhaseEvent;
import javax.faces.event.PhaseId;
import javax.faces.event.PhaseListener;

import oracle.adf.controller.internal.binding.DCTaskFlowBinding;
import oracle.adf.model.BindingContext;
import oracle.adf.model.binding.DCBindingContainer;
import oracle.adf.model.binding.DCDataControl;

import oracle.adf.view.rich.context.AdfFacesContext;

import oracle.binding.BindingContainer;

import oracle.ui.pattern.dynamicShell.Tab;
import oracle.ui.pattern.dynamicShell.TabContext;

import org.apache.log4j.Logger;

/**
* JHeadstart JSF PhaseListener.
* When using Dynamic tabs template, the current tab state is set to dirty or clean,
* depending on the state of the taskflow data control of the current tab
*/
public class JhsPhaseListener
implements PhaseListener
{

private static Logger sLog = Logger.getLogger(JhsPhaseListener.class);

public static final String PAGE_TEMPLATE_BINDING = "pageTemplateBinding";

@Override
/**
* While refreshing the curent tab seems more effective to do only just before render response
* it turns out that when closing a tab, and then returning to a dirty tab, the data control
* of the dirty tab is no longer seen as dirty, so we do it after the three phases that can
* change the state of the current tab: apply request values, update model values and invoke application
*/
public void afterPhase(PhaseEvent event)
{
if (event.getPhaseId() == PhaseId.APPLY_REQUEST_VALUES
|| event.getPhaseId() == PhaseId.UPDATE_MODEL_VALUES
|| event.getPhaseId() == PhaseId.INVOKE_APPLICATION)
{
checkCurrentTabDirtyState();
}
}

@Override
public void beforePhase(PhaseEvent event)
{
}

@Override
public PhaseId getPhaseId()
{
return PhaseId.ANY_PHASE;
}

public void checkCurrentTabDirtyState()
{
TabContext tabContext = TabContext.getCurrentInstance();
if (tabContext == null || tabContext.getSelectedTabIndex() < 0)
{
return;
}
BindingContainer bc =BindingContext.getCurrent().getCurrentBindingsEntry();
if (bc==null)
{
return;
}
DCBindingContainer pageTemplateBc =
(DCBindingContainer) bc.get(PAGE_TEMPLATE_BINDING);
if (pageTemplateBc == null)
{
return;
}
DCTaskFlowBinding tfb =
(DCTaskFlowBinding) pageTemplateBc.get("r" + tabContext.getSelectedTabIndex());
if (tfb == null || tfb.getExecutableBindings() == null ||
tfb.getExecutableBindings().size() == 0)
{
return;
}
DCBindingContainer taskFlowBc =
(DCBindingContainer) tfb.getExecutableBindings().get(0);
DCDataControl dc = taskFlowBc.getDataControl();
if (dc==null)
{
// no data control, we cannot detect pending changes
return;
}
boolean isDirty = dc != null && (dc.isTransactionDirty() || dc.isTransactionModified());
// calling covenience method markCurrentTabDirty adds content area as partial target,
// causing any popups currently displayed to be hidden again.
// Therefore retrieve current tab instance and call setDirty directly
// tabContext.markCurrentTabDirty(isDirty);
Tab tab = tabContext.getTabs().get(tabContext.getSelectedTabIndex());
if (tab.isDirty() != isDirty)
{
sLog.debug("Setting dirty state of dynamic tab with index " +
tab.getIndex() + " to " + isDirty);
tab.setDirty(isDirty);
AdfFacesContext.getCurrentInstance().addPartialTarget(tabContext.getTabsNavigationPane());
}
}
}

Just copy and past this code to create your own phase listener class, and register the phase listener in faces-config.xml like this (substitute with your own class name):


<lifecycle>
<phase-listener>oracle.jheadstart.controller.jsf.listener.JhsPhaseListener</phase-listener>
</lifecycle>

And make sure that the taskflows that are accessed through the dynamic tabs have the data-control-scope element set to shared.
That's all you need to do to enjoy this nice functionality.

Comments:

Nice blog. But can this solution be used with taskflows with data-control-scope set to Isolated. I did check but it is showing up really weird errors. Your thoughts on this?

Posted by guest on August 28, 2011 at 04:56 PM PDT #

Yes, see new post on this topic which has better implementation of marking tab dirty, sample app with data control isolated is included:
http://blogs.oracle.com/jheadstart/entry/core_adf11_uishell_with_dynamic

Posted by Steven Davelaar on October 13, 2011 at 07:23 AM PDT #

Post a Comment:
  • HTML Syntax: NOT allowed
About

Java EE Consultants - JHeadstart, ADF, JSF

Search

Archives
« April 2014
SunMonTueWedThuFriSat
  
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
   
       
Today