This post will cover the coding details of implementing an ADF XML menu model.
Do you need to manage all your navigation menus in a centralized file including its label, URL, title and other custom attributes you may have? If so, the solution is to use ADF XML menu model.
ADF XML menu model is the same concept as the built in WebCenter Navigation Model in Oracle WebCenter Portal. It uses a XML file to hold the navigation metadata - id, label, title, destination/url and other custom attributes you may need.
The XML file is the data model layer. Using XML menu, you can generate various kinds of navigation user interface components, such as navigation links, bars, tabs, breadcrumbs, etc. You can also apply business logic to conditionally control the rendering of the navigation menu. The business logic including security can be applied at either the XML model layer or the UI presentation layer depending on where it fits your purpose. You can create multiple navigation hierarchy (nested navigations) in your XML menu model.
Without further ado, here are the details you like to know on implementation of such XML menu model in ADF application.
1. Create ADF Menu Model
ADF menu model is created on top of the unbounded task flow in application. By default one application comes with one unbounded task flow, but you can create additional if needed. If you have a use case requiring different independent sets of menu models, then you should create additional unbounded task flows as the unbounded task flow and menu model go as 1 to 1. At ADF runtime, multiple unbounded task flows will be compiled into one for the application they sit in.
Select the target unbounded task flow, right click on it and select "Create ADF menu model...". It would create an XML file placeholder.
2. Update Model with Your Data
The generated xml file is where you need to compile your navigation data. It comes with a top level "menu" element and inside it, it can be "itemNode", "groupNode" or "sharedNode".
Do you need to manage all your navigation menus in a centralized file including its label, URL, title and other custom attributes you may have? If so, the solution is to use ADF XML menu model.
ADF XML menu model is the same concept as the built in WebCenter Navigation Model in Oracle WebCenter Portal. It uses a XML file to hold the navigation metadata - id, label, title, destination/url and other custom attributes you may need.
The XML file is the data model layer. Using XML menu, you can generate various kinds of navigation user interface components, such as navigation links, bars, tabs, breadcrumbs, etc. You can also apply business logic to conditionally control the rendering of the navigation menu. The business logic including security can be applied at either the XML model layer or the UI presentation layer depending on where it fits your purpose. You can create multiple navigation hierarchy (nested navigations) in your XML menu model.
Without further ado, here are the details you like to know on implementation of such XML menu model in ADF application.
1. Create ADF Menu Model
ADF menu model is created on top of the unbounded task flow in application. By default one application comes with one unbounded task flow, but you can create additional if needed. If you have a use case requiring different independent sets of menu models, then you should create additional unbounded task flows as the unbounded task flow and menu model go as 1 to 1. At ADF runtime, multiple unbounded task flows will be compiled into one for the application they sit in.
Select the target unbounded task flow, right click on it and select "Create ADF menu model...". It would create an XML file placeholder.
2. Update Model with Your Data
The generated xml file is where you need to compile your navigation data. It comes with a top level "menu" element and inside it, it can be "itemNode", "groupNode" or "sharedNode".
- itemNode: Specifies a node that performs navigation upon user selection. An itemNode can have children itemNodes.
- groupNode: Groups child components; the
groupNode
itself does no navigation. Child nodes node can beitemNode
or anothergroupNode
. - sharedNode: References another
XMLMenuModel
instance. AsharedNode
element is not a true node; it does not perform navigation nor does it render anything on its own.
An itemNode comes with a variety of build-in metadata. But they still can fall short for your use cases. A good thing about this model is you can create as many as custom attributes as needed. For example:
<itemNode id="exmapleId" label="#{bundle.EXAMPLELABEL}" rendered="#{sessionScope.IsRendered eq 'Y'}" url="/faces/mypage"/>
As exampled, you can use expression language as the attribute values.
3. Create Managed Bean for the Menu Model
During step 1, when creating the ADF menu model, a managed bean for XMLMenuModel will be automatically configured in faces-config.xml. This is all good if you don't have any custom attributes in the menu model. But in case you do, here is the info you like to refer to.
Create a Java bean extending "org.apache.myfaces.trinidad.model.XMLMenuModel", and in the bean, you will need to cover some key methods to expose the navigation node and custom attributes. Here is a pretty comprehensive example:
In the example, the menu model instance is retrieved by expression "#{portal_menu}" which you can define it in the unbounded task flow in a request scope.
4. Render the Navigation on UI
The last step is to put the XML menu model data reference into the UI layer. There are quite a few different UI components that can be used for this. Here is the cheat sheet for the ADF menu components.
Alternatively, you can use <af:iterator> to iterate through the menu model and render them using a simple go link. For example:
Create a Java bean extending "org.apache.myfaces.trinidad.model.XMLMenuModel", and in the bean, you will need to cover some key methods to expose the navigation node and custom attributes. Here is a pretty comprehensive example:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 | private static Object getNodeById(XMLMenuModel menuModel, String nodeId) { Object returningNode = null; FacesContext context = FacesContext.getCurrentInstance(); ELResolver resolver = context.getApplication().getELResolver(); ELContext elContext = context.getELContext(); if (menuModel == null) { return returningNode; } for ( int i = 0; i < menuModel.getRowCount(); i++) { menuModel.setRowIndex(i); Object parentNode = menuModel.getRowData(); String parentNodeId = resolver.getValue(elContext, parentNode, "id").toString(); if (parentNodeId.equals(nodeId)) { returningNode = parentNode; break; } if (menuModel.isContainer() && !menuModel.isContainerEmpty()) { menuModel.enterContainer(); for ( int j = 0; j < menuModel.getRowCount(); j++) { menuModel.setRowIndex(j); Object childNode = menuModel.getRowData(); String childNodeId = resolver.getValue(elContext, childNode, "id").toString(); if (childNodeId.equals(nodeId)) { returningNode = childNode; break; } } menuModel.exitContainer(); } } return returningNode; } public static String getUrlById(String nodeId) { PortalMenuModel menuModel = (PortalMenuModel)ADFUtils.evaluateEL("#{portal_menu}"); Object node = getNodeById(menuModel, nodeId); if (node == null) { return ""; } String url = menuModel.getCustomProperty(node, "url").toString(); url += "&" + getNodeIdUrlParam(node); return url; } public Map<String, String> getUrlById() { return new HashMap<String, String>() { @Override public String get(Object key) { String str = (String)key; str = getUrlById(str); return str; } }; } public static String getLabelById(String nodeId) { PortalMenuModel menuModel = (PortalMenuModel)ADFUtils.evaluateEL("#{portal_menu}"); Object node = getNodeById(menuModel, nodeId); if (node == null) { return ""; } FacesContext context = FacesContext.getCurrentInstance(); ELResolver resolver = context.getApplication().getELResolver(); ELContext elContext = context.getELContext(); return resolver.getValue(elContext, node, "label").toString(); } public Map<String, String> getLabelById() { return new HashMap<String, String>() { @Override public String get(Object key) { String str = (String)key; str = getLabelById(str); return str; } }; } public static String findURLByIdWithContextRoot(String nodeId) { return HTTPUtils.getContextPath()+getUrlById(nodeId); } public static void redirectToPageLinkId(String nodeId) { FacesContext fctx = FacesContext.getCurrentInstance(); ExternalContext ectx = fctx.getExternalContext(); try { String url = getUrlById(nodeId); ectx.redirect( HTTPUtils.getRequest().getContextPath() + url); fctx.responseComplete(); } catch (IOException e) { logger.error("Exception occurred in PortalMenuModel while redirecting without url params:" + e.getMessage()); } } public static void redirectToPageLinkId(String nodeId, Map<String,String> urlParam) { FacesContext fctx = FacesContext.getCurrentInstance(); ExternalContext ectx = fctx.getExternalContext(); try { Iterator<String> paramIterator = urlParam.keySet().iterator(); StringBuilder sb = new StringBuilder(); while(paramIterator.hasNext()) { String key = paramIterator.next(); sb.append("&"+ key + "=" + urlParam.get(key) ); } String url = getUrlById(nodeId); ectx.redirect( HTTPUtils.getRequest().getContextPath() + url + sb.toString()); fctx.responseComplete(); } catch (IOException e) { logger.error("Exception occurred in PortalMenuModel while redirecting with url params:" + e.getMessage()); } } public static String getNodeIdUrlParam (Object node) { FacesContext context = FacesContext.getCurrentInstance(); ELResolver resolver = context.getApplication().getELResolver(); ELContext elContext = context.getELContext(); String nodeIdParam = resolver.getValue(elContext, node, "uniqueId").toString(); return NODE_ID_PARAM + "=" + nodeIdParam; } |
In the example, the menu model instance is retrieved by expression "#{portal_menu}" which you can define it in the unbounded task flow in a request scope.
4. Render the Navigation on UI
The last step is to put the XML menu model data reference into the UI layer. There are quite a few different UI components that can be used for this. Here is the cheat sheet for the ADF menu components.
Alternatively, you can use <af:iterator> to iterate through the menu model and render them using a simple go link. For example:
1 2 3 4 5 | <af:iterator var="node" id="i1" value="#{portal_menu}"> <af:goLink destination="#{portal_menu.urlById[node.id]}"> <af:outputText id="ot1" value="#{Bundle[node.customPropList['navTitle']]}" escape="false"/> </af:goLink> </af:iterator> |
Comments