Problem Description: Embeding Regions Inside Popup Windows 2.8
Problem Description: Embeding Regions Inside Popup Windows 2.8
Problem Description: Embeding Regions Inside Popup Windows 2.8
PopUp Windows 2.8 JDeveloper 11g, ADF, ADF Faces, ADF Controller Pattern Team September 19. 2008 May 21, 2009
Problem Description
Regions can be incorporated into ADF Faces popup content to support potentially complex navigation through a series of pages to complete a task. Another important benefit of including regions within ADF Faces popups is content reuse. These great benefits make ADF Faces popups containing regions a commonly used application development pattern. Incorporating regions into <af:popup> content can seem similar to incorporating regions into a page, especially since an <af:popup> is actually considered part of a page. However, there are some important differences to keep in mind. When incorporating regions into <af:popup> content, the following default development constraints must be taken into consideration: <af:popup> content is created and executed when its corresponding page is initially displayed. Its not automatically refreshed when a <af:popup> is disclosed. When disclosing an <af:popup> subsequent times from the same page, region content isnt automatically refreshed and restarted from the beginning. An <af:popup> is not automatically dismissed when its region content completes via navigation flow. Likewise, when an <af:popup> is dismissed, its corresponding region content is not automatically considered complete. Therefore, resources used by the corresponding task flow (e.g. memory, DB transactions, etc.) are not released. Embeding Regions inside PopUp Windows
Functional Region components activate when their <af:region> UI component is invoked (component tree built), not when the component tree is rendered. Most JSF UI components build their component tree and wait until rendering to activate. The <af:region> UI component is different since it must first resolve its task flows target view to include the corresponding page fragment. Many of these default development constraints are the result of <af:popup> behavior being mainly client-side only. When a user discloses an <af:popup>, JavaScript on the client unhides the <af:popup> created previously when the page was initially displayed. When the <af:popup> is dismissed, JavaScipt on the client simply hides the <af:popup> again. In both cases, no event or request is sent to the server to refresh the region content or retrieve data based on the current application state. If the <af:popup> is displayed a second time, it simply redisplays the <af:popup> content remaining from the previous disclosure.
Functional 6. Once again, the <af:popup> containing the region is disclosed. Region content is refreshed and restarted from the beginning. All content displayed within the <af:popup> is based on the current application state when the <af:popup> is disclosed the second time. It does not redisplay content from the previous disclosure.
The Artifacts
When incorporating regions into <af:popup> content, the following development constructs are recommend to implement the desired use case behavior: Dynamic Region used instead of a region to swap between an empty task flow and the real task flow. Ensures resources and memory for the real task flow are only allocation when the <af:popup> is disclosed and appropriately released when the <af:popup> is dismissed. <af:popup> contentDelivery controls when the <af:popup> delivers its content to the browser. When set to "lazyUncached" the <af:popup> markup is pulled down each time the <af:popup> is disclosed. <af:setPropertyListener> specified on the <af:popup> to swap the real task flow into the dynamic region when a popupFetch event is received. <af:region> regionNavigationListener defined on the <af:region> UI component to identify when the region completes (task flow exits via Embeding Regions inside PopUp Windows
Functional navigation flow to a task flow return activity). When executed includes logic to automatically hide the <af:popup>. If the region is not designed to exit, a regionNavigationListener is not required. <af:serverListener> specified on the <af:popup> to swap an empty task flow into the dynamic region when a popupClosed event is received.
Note: Dynamic regions (and other regions) are initially refreshed when
their parent page is first displayed. This includes those appearing within Embeding Regions inside PopUp Windows
Functional <af:popup> content. taskFlow Binding Refresh and RefreshCondition settings only control refreshing after the initial refresh. Therefore, application developers should ensure dynamic regions (or other regions) appearing within <af:popup> content dont result in errors due to unavailable data when the parent page is initially displayed. This is another benefit of assigning an empty task flow to a dynamic region appearing within <af:popup> content when the parent page is first displayed.
Functional
} public TaskFlowId getPopupTaskFlowId() { return TaskFlowId.parse(popupTaskFlowId); } }
<af:popup> contentDelivery
To ensure <af:popup> markup will be delivered refreshed each time the <af:popup> is disclosed its contentDelivery attribute is set to lazyUncached. The contentDelivery default lazy only delivers markup when the <af:popup> is first disclosed and then caches it. The <af:popup> attributes launchVar and eventContext should be set as shown in the example below so popupFetch and popupClosed events produced when an <af:popup> is disclosed and dismissed can be utilized. These events provide the timing for when to swap the dynamic region taskFlowId.
<af:popup id="popupRegion1" contentDelivery="lazyUncached" launcherVar="source" eventContext="launcher"> <af:panelWindow id="window" title="Employee Popup" modal="true"> </af:panelWindow> </af:popup>
into <af:popup> content an <af:panelWindow> should be used within the <af:popup>. An <af:popup> without an <af:panelWindow> is an inline selector popup with auto-dismissal behaviors making it unusable with dynamic regions (or other regions). If an < af:dialog> is used within the <af:popup> instead of an <af:panelWindow>, connecting the <af:dialog> buttons in the footer up with the task flow navigation in the dynamic region is a very complicated endeavor. Its much easier to use an <af:panelWindow> and incorporate any buttons into the dynamic region content. Embeding Regions inside PopUp Windows
Functional
<af:setPropertyListener>
An <af:setPropertyListener> is specified within an <af:popup> to swap the real task flow into a dynamic region when a popupFetch event is received just before the <af:popup> is disclosed. An example of swapping the popupTaskFlowId value into the dynamicTaskFlowId when a popupFetch event is received is shown below. For further details on these managed bean properties, refer to the Dynamic Region Managed Bean section of the document. An <af:setPropertyListener> can also be specified within an <af:popup> to pass values from the parent page set as an <af:clientAttribute>. These values might be required for dynamic region input parameters. The <af:setPropertyListener> retrieves these values from the <af:popup> launcher component when a popupFetch event is received. An example of retrieving the passed value of the <af:clientAttribute> employee from the launcher component is shown below.
<af:popup id="popupRegion1" contentDelivery="lazyUncached" launcherVar="source" eventContext="launcher"> <af:setPropertyListener from="#{source.attributes.employee}" to="#{pageFlowScope.employeeId}" type="popupFetch"/> <af:setPropertyListener from="#{viewScope.PopupDynamicRegionBean.popupTaskFlowId}" to="#{viewScope.PopupDynamicRegionBean.dynamicTaskFlowId}" type="popupFetch"/> <af:panelWindow id="window" title="Employee Popup" modal="true"> </af:panelWindow> </af:popup>
<af:region> regionNavigationListener
An <af:region> regionNavigationListener is required if the dynamic region within the <af:popup> provides the ability to exit via navigation flow to task flow return activities. The regionNavigationListener identifies when the region exits so the <af:popup> can be programmatically dismissed without the user manually selecting its close icon. Using the employee-update task flow definition below as an example, the regionNavigationListener would be performed after executing either the save or cancel task flow return activities to exit the task flow definition.
Functional
The regionNavigationListener method used to programmatically hide the <af:popup> when the task flow exists would be similar to the following:
public void navigationListener(RegionNavigationEvent event){ String newViewId = event.getNewViewId();
Functional
// null new view id indicates the taskflow has ended if (newViewId == null) { RichRegion region = (RichRegion)event.getSource(); // look for the parent popup boolean found = false; UIComponent component = region.getParent(); do { if (component instanceof RichPopup) { found = true; } else { component = component.getParent(); if (component == null) { break; } } } while (!found); if (found) { // send script to the client to hide the popup FacesContext context = FacesContext.getCurrentInstance(); Service.getRenderKitService(context, ExtendedRenderKitService.class).addScript(context, "var popup = AdfPage.PAGE.findComponent('" + component.getClientId(context) + "'); popup.hide();"); } } }
<af:severListener>
An <af:severListener> is specified on the <af:popup> to swap an empty task flow into the dynamic region when a popupClosed event is received. But, since the popupClosed
Functional event is a client side only event, an <af:clientListener> is also implemented to identify the popupClosed event then launch a custom event the <af:serverListner> can receive.
<af:popup id="popupRegion1" contentDelivery="lazyUncached" launcherVar="source" eventContext="launcher"> <af:setPropertyListener from="#{source.attributes.employee}" to="#{pageFlowScope.employeeId}" type="popupFetch"/> <af:setPropertyListener from="#{viewScope.PopupDynamicRegionBean.popupTaskFlowId}" to="#{viewScope.PopupDynamicRegionBean.dynamicTaskFlowId}" type="popupFetch"/> <af:panelWindow id="window" title="Employee Popup" modal="true"> <af:region value="#{bindings.dynamicRegion1.regionModel}" id="dynam1" regionNavigationListener="#{viewScope.PopupDynamicRegionBean.navigationListener}"/> </af:panelWindow> <af:clientListener method="popupClosedListener" type="popupClosed"/> <af:serverListener type="serverPopupClosed" method="#{viewScope.PopupDynamicRegionBean.swapEmptyTaskFlow}"/> </af:popup>
The following <script> is incorporated into the page containing the <af:popup> for the <af:clientListener> to perform when a popupClosed event is received. It invokes a server side callback custom event the <af:serverListener> can receive. Instead of incorporating JavaScript into the page, a custom JSP tag can be implemented to encapsulate the same behavior. For further details on implementing a custom JSP tag, refer to Appendix Implementing a Custom JSP Tag.
<script> function popupClosedListener(event) { var source = event.getSource(); var popupId = source.getClientId(); var params = {}; params['popupId'] = popupId;
Functional
var type = "serverPopupClosed"; var immediate = true; AdfCustomEvent.queue(source, type, params, immediate); } </script>
The method called by the <af:serverListener> to swap the empty task flow into the dynamic region would be similar to the following:
public void swapEmptyTaskFlow(ClientEvent event) { setDynamicTaskFlowId(""); // if event delivery set to immediate=true, short-circuit to renderResponse. // Forcing an empty taskflow releases the bindings and view port. FacesContext context = FacesContext.getCurrentInstance(); context.renderResponse(); String popupId = (String) event.getParameters().get("popupId"); System.out.println("**** Swapping Empty Taskflow on popupClosed for " + popupId + " ****"); }
Interaction Behavior
<af:popup> Disclosed
1. Button/link is selected and invokes <af:showPopupBehavior> to identify the appropriate <af:popup> to disclose. 2. <af:popup> popupFetch event published. 3. <af:popup> popupFetch event received by <af:setPropertyListener>. 4. <af:setPropertyListener> assigns <af:clientAttribute> values from the parent page to pass into the <af:popup>. 5. <af:setPropertyListener> swaps the real task flow into the dynamic region. Embeding Regions inside PopUp Windows
Functional 6. taskFlow Binding of the dynamic region is refreshed and restarted since its taskFlowId has been changed. 7. <af:popup> popupFetch event delivers new markup to the browser and displays it to the user since <af:popup> contentDelivery=lazyUncached.
Functional 9. Custom event for server side callback is received by <af:serverListener>. 10. <af:serverListener> method swaps an empty task flow into the dynamic region. 11. taskFlow Binding of the dynamic region is refreshed and restarted since its taskFlowId has been changed. Memory and resources remaining from the previous taskFlowId are released appropriately. 12. If event delivery set to immediate=true, jump is forced to renderResponse phase. 13. Next time user discloses the <af:popup> its content will once again be refreshed.
Functional
method="#{viewScope.PopupDynamicRegionBean.swapEmptyTaskFlow}"/> </af:popup>
The #{viewScope.PopupDynamicRegionBean.swapEmptyTaskFlow} method specified by the custom JSP tag would be similar to the following:
public void swapEmptyTaskFlow(ClientEvent event) { setDynamicTaskFlowId(""); // if event delivery set to immediate="true", short-circuit to renderResponse. // Forcing an empty taskflow releases the bindings and view port. Boolean immediate = (Boolean)event.getParameters().get("immediate"); if (immediate != null && immediate) { FacesContext context = FacesContext.getCurrentInstance(); context.renderResponse(); } String popupId = (String)event.getParameters().get("popupId"); System.out.println("**** Swapping Empty Taskflow on popupClosed for " + popupId + " ****"); }
To create the custom JSP tag described above, the following items should be implemented: JSP Tag Library (*.tld) describes design time behavior of the custom JSP tag. Custom Tag Behavior Class (*.java) describes server side behavior of the JSP custom tag. Custom Tag Behavior JavaScript (*.js) describes client side behavior of the JSP custom tag. Resources (adfdemo.resources) specifies servlet resource files to include. No need to create if existing servlet resource already defined.
Functional ResourceLoader (*.java) identifies resource loaders for handling JavaScript files in the resources servlet. No need to create if existing file already defined. web.xml servlet mapping. No need to create if existing servlet mapping for appropriate servlet already specified. Project Properties - compiler Settings, Trinidad Runtime Library, and Trinidad Tag Library. No need to add if existing file inclusion types and libraries are already specified. Script Delivery Using <trh:script> - No need to add to page markup if already specified.
Functional
<deferred-method> <method-signature>void myMethod(oracle.adf.view.rich.render.ClientEvent)</methodsignature> </deferred-method> </attribute> </tag> </taglib>
Functional
AdfDemoClosePopupDynamicRegionBehavior('").append(popupId).append("',") .append(immediate).append(")"); return buff.toString(); } public void setPopupId(String popupId) { this.popupId = popupId; } public String getPopupId() { return popupId; } public void setImmediate(Boolean immediate) { this.immediate = immediate; } public Boolean getImmediate() { return immediate; } public void setMethod(MethodExpression method) { this.method = method; } public MethodExpression getMethod() { return method; } protected void updateClientListenerSet(UIComponent component, ClientListenerSet cls)
Functional
{ super.updateClientListenerSet(component, cls); if (method != null) { cls.addCustomServerListener("serverPopupClosed", method); } } }
Functional
var immediate = this._immediate; AdfAssert.assertBoolean(immediate); var params = {};
params['popupId'] = popupId; var type = "serverPopupClosed"; AdfCustomEvent.queue(source, type, params, immediate); } AdfDemoClosePopupDynamicRegionBehavior.prototype.Init = function (popupId, immediate) { AdfDemoClosePopupDynamicRegionBehavior.superclass.Init.call(this); AdfAssert.assert(popupId != null); this._popupId = popupId; this._immediate = immediate; }
Resources - adfdemo.resources
\ViewController\src\META-INF\servlets\resources\adfdemo.resources
oracle.adf.view.ResourceLoader
ResourceLoader - ResourceLoader.java
\ViewController\src\oracle\adf\view\ResourceLoader.java
package oracle.adf.view; import org.apache.myfaces.trinidad.resource.AggregatingResourceLoader; import org.apache.myfaces.trinidad.resource.ClassLoaderResourceLoader; import org.apache.myfaces.trinidad.resource.RegexResourceLoader; public class ResourceLoader
Functional
extends RegexResourceLoader { public ResourceLoader() { register("(/.*\\.(jpg|gif|png|jpeg))", new ClassLoaderResourceLoader("META-INF")); register("(/.*all-adfdemo.js)", new AcmeScriptsResourceLoader("/adfdemo/all-adfdemo.js")); } public static class AcmeScriptsResourceLoader extends AggregatingResourceLoader { public AcmeScriptsResourceLoader(String scriptsURI) { super(scriptsURI, _LIBRARIES, new ClassLoaderResourceLoader()); this.setSeparator(AcmeScriptsResourceLoader._NEWLINE_SEPARATOR); } static private final String[] _LIBRARIES = { "oracle/adf/view/AdfDemoClosePopupDynamicRegionBehavior.js" }; static private final String _NEWLINE_SEPARATOR = "\n"; } }
Functional
</servlet-mapping>
Project Properties
Add file inclusion types to project properties complier "Copy File Types to Output Directory" field for bundling in .resource and .js. The Trinidad Tag Library should be added to the project properties JSP Tag Libraries and the Trinidad Runtime Libraries to the Libraries and Classpath.
Known Issues
Automatic Partial Page Rendering: Automatic Partial Page Rendering for dynamic regions within <af:popups> is not supported by ADF Faces and ADF Controller. The "ChangeEventPolicy" attribute on the iterator bindings and value bindings should be set to 'none'. Instead, ADF Faces Partial Page Rendering can be used. For further information on ADF Faces Partial Page Rendering, refer to the "Oracle Fusion Middleware Web User Interface Developer's Guide for Oracle Application Development Framework 11g Release" Chapter 7 "Rendering Partial Page Content".
Functional