View driven accordeon
The Extension Library features a Dojo Accordion control. In the sample application the panels and their content are created static using
Working my way backwards I first designed the format for the SSJS I need for the menu and then how to generate this format from an categorized view. The format that suited this need looks like this:
In a live application, the
This child then contains one
In theory that all looks brilliant, except the inner repeatTreeNode would not get access to the repeat variable from the parent basicContainerNode.
This has been accnowledged as a bug and is tracked as SPR # MKEE9SKENK
(I'm, inside IBM, outside the development team, the undisputed XPages SPR champion).
So a little hack was necessary to make that menu work.The result is this:
The panel above the accordeon is to manage the cache of my menu code.
Depending on most use cases the menu is quite static and it doesn't make sense to read and read and read it from the database.
The clear button belongs to an admin panel, so after a menu edit, the cache can be cleared there.
Also using the
In my SSJS I read the whole view based on the view name. In your application you might hardcode the view and use the name to lookup a category of entries and render only the subentries. Whatever suits you.
As you can see, I use a naming convention that would allow me to swap the SSJS out for a managed bean. I strongly encourage you to follow this.
Enjoy - and as usual YMMV
xe:basicContainerNode
and xe:basicLeafNode
. A customer asked: " Can I populate the accordion using view entries?"
Working my way backwards I first designed the format for the SSJS I need for the menu and then how to generate this format from an categorized view. The format that suited this need looks like this:
[
{ name : "Level 1"; items : ["red 1":"blue 1":"green 1"]},
{ name : "Level 2"; items : ["red 2":"blue 2"]},
{ name : "Level 3"; items : ["red 3":"green 3"]}
]
In a live application, the
items
rather would be objects with a label and an action ( items : [{"label": "red 2", "action" : "paintred"}:{"label" : "blue 2", "action" : "playBlues" }]
) or URL, but for the demo a string is sufficient. I created a accordeon with a xe:repeatTreeNode
containing a xe:basicContainerNode
as sole child element (which would repeat).
This child then contains one
xe:repeatTreeNode
that would again contain one xe:basicContainerNode
as sole child (which again would repeat).
In theory that all looks brilliant, except the inner repeatTreeNode would not get access to the repeat variable from the parent basicContainerNode.
This has been accnowledged as a bug and is tracked as SPR # MKEE9SKENK
(I'm, inside IBM, outside the development team, the undisputed XPages SPR champion).
So a little hack was necessary to make that menu work.The result is this:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xe="http://www.ibm.com/xsp/coreex">
<xp:this.resources>
<xp:script src="/MenuTools.jss" clientSide="false"></xp:script>
</xp:this.resources>
<xp:panel>
<xp:text escape="true" id="computedField1"
value="#{javascript:sessionScope.menuList}">
</xp:text>
<xp:button value="Clear cache" id="button1">
<xp:eventHandler event="onclick" submit="true"
refreshMode="complete">
<xp:this.action><![CDATA[#{javascript:sessionScope.remove('menuList');}]]></xp:this.action>
</xp:eventHandler></xp:button>
</xp:panel>
<xe:accordion id="accordion1" style="width: 400px; height:600px">
<xe:this.treeNodes>
<xe:repeatTreeNode var="tree" indexVar="index"
value="#{javascript:menuTools.getMenu(database,'Menu');}">
<xe:this.children>
<xe:basicContainerNode>
<xe:this.label><![CDATA[#{javascript:requestScope.currNode = tree; tree.name}]]></xe:this.label>
<xe:this.children>
<xe:repeatTreeNode var="subtree"
indexVar="subindex"
value="#{javascript:requestScope.currNode.items}">
<xe:this.children>
<xe:basicLeafNode
label="Leaf #{subtree}">
</xe:basicLeafNode>
</xe:this.children>
</xe:repeatTreeNode>
</xe:this.children>
</xe:basicContainerNode>
</xe:this.children>
</xe:repeatTreeNode>
</xe:this.treeNodes>
</xe:accordion>
</xp:view>
The panel above the accordeon is to manage the cache of my menu code.
Depending on most use cases the menu is quite static and it doesn't make sense to read and read and read it from the database.
The clear button belongs to an admin panel, so after a menu edit, the cache can be cleared there.
Also using the
applicationScope
will improve performance, since only the very first user of the application will actually read the database.
In my SSJS I read the whole view based on the view name. In your application you might hardcode the view and use the name to lookup a category of entries and render only the subentries. Whatever suits you.
var menuTools = {
"getMenu" : function(nsf:NotesDatabase, menuName:string) {
/*Checks cache first could be session or applicationScope*/
if (!sessionScope.menuList) {
sessionScope.put("menuList", new java.util.HashMap());
}
if (sessionScope.menuList.containsKey(menuName)) {
return sessionScope.menuList.get(menuName);
}
/*Retrieves menu entries based on a view name*/
var v:NotesView = nsf.getView(menuName);
var vn:NotesViewNavigator = v.createViewNav();
var ve = vn.getFirst();
var result = [];
while (ve != null) {
var nextVe = vn.getNextSibling(ve);
var lineResult = {};
lineResult.name = ve.getColumnValues()[0];
lineResult.items = [];
var child = vn.getChild(ve);
while (child != null) {
var nextChild = vn.getNextSibling(child);
lineResult.items.push[child.getColumnValues(](1));
child.recycle();
child = nextChild;
}
result.push(lineResult);
ve.recycle();
ve = nextVe;
}
try {
vn.recycle();
v.recycle();
} catch (e) {
print(e);
}
// Save it to the cache
sessionScope.menuList.put(menuName, result);
return result;
}
}
As you can see, I use a naming convention that would allow me to swap the SSJS out for a managed bean. I strongly encourage you to follow this.
Enjoy - and as usual YMMV
Posted by Stephan H Wissel on 08 January 2015 | Comments (0) | categories: XPages