dominoGuru.com
Your Development & Design Resource
scriptBlock Artifacting for IBM XPages
05/15/2012 05:45:00 PM by Chris Toohey
I've been working on a few IBM XPages-based application frameworks lately (some basic templates to help jumpstart application development for project work), and have been using Dojo contentPanes (dijit.layout.ContentPane) with great success. One of the features of the Dojo ContentPane is the ability to load content via XHR (XMLHttpRequest). This tactic allows you to use ultimately surface same-domain contents inside of your ContentPane as if they were native to the current application.
Combining this ContentPane tactic with an ExpandoPane (dojox.layout.ExpandoPane)-based BorderContainer (dijit.layout.BorderContainer), I was able to create an amazingly functional and slick, yet totally familiar, shell for my XPages applications that required a more traditional application layout vs. a website-style layout.
In other words, my layout looked like this:
The yellow area in the above layout mockup is the aforementioned ContentPane, and the blue area highlights the ExpandoPane-based navigator.
Loading an XPage Form or View in the ContentPane via XHR was simple, really - once you understood that XPages would deliver the markup, and all you really needed to do was initiate the Dojo Parser to re-Dojo-ify things. Hell, loading the given Form or View into the target ContentPane and calling that parse was really just a simple ClientSide JavaScript function:
var setFrameHref = function(tfKey, tfHref) {
var t = dijit.byId(tfKey);
t.attr("onDownloadEnd", function() {
dojo.parser.parse();
});
t.attr("href", tfHref);
}
Everything was going well until I realized that I needed to update my navigator when I performed some CRUD function via the ContentPane... but the ContentPane had no relationship with the navigator context.
... ok, that might not make sense to you now, but I'll explain.
The ContentPane is basically an IFRAME HTML Element. It loads whatever content via XHR you set, and that becomes part of the HTML Document Object Model.
If I was not using a ContentPane, or specifically not using the HREF property/XHR tactic for loading an XPage form or view, this really wouldn't be an issue. Typically, you compute an xp:panel Control, throw the desired data source details into scoped variables, and you use partial refreshes to dynamically load your contents.
I needed to update the navigator. Sure, I could use multiple partial refreshes -- the first on the computed xp:panel Control that was my XPage form and a second on the navigator itself.
But since the contents of the ContentPane have absolutely no idea of the container layer -- just like an IFRAME HTML Element wouldn't know when-rendered what any of the parent contents are -- that simply won't work.
It's not like you can define "container_navigator" as the partial refresh target in the XPage form. Remember, the navigator is in an entirely separate XPage (might as well be a different NotesDatabase while we're at it...).
To sum up, the XHR-loaded XPages have no idea that the partial refresh target for the navigator is "container_navigator". And you can't just assume the HTML Element ID, because that changes. It really needs to be the result of #{id:container_navigator}
.
So that's where scriptBlock artifacting comes into play!
<xp:scriptBlock>
<xp:this.value><![CDATA[function refreshMenu_applications() {
XSP.partialRefreshGet('#{id:container_menu_settings_applications}',{});
setFrameHref('#{id:NotesView}', 'welcome.xsp');
}]]></xp:this.value>
</xp:scriptBlock>
This xp:scriptBlock Control resides in my layout.xsp Custom Control. It knows exactly what the IDs of my given layout containers are since it shares the same context. It's sole purpose is to handle navigator refreshing... and it's all Client Side JavaScript that sits there waiting to be called by some external object or process.
Here's the Save/Update Application Profile button XPage markup, which puts the scriptBlock artifact function to use:
<xp:button
value="Save/Update Application Profile"
styleClass="save"
id="button_save">
<xp:eventHandler
event="onclick"
submit="true"
refreshMode="norefresh"
immediate="false"
save="false">
<xp:this.action><![CDATA[#{javascript:CRUD(thisapp, 'create');}]]></xp:this.action>
<xp:this.onComplete><![CDATA[refreshMenu_applications();]]></xp:this.onComplete>
</xp:eventHandler>
</xp:button>
This button executes the Server Side function, and onComplete calls the waiting refreshMenu_applications() Client Side JavaScript function.
If you're still thinking, "... wha?!" -- don't worry, it's one of those "the demo explains this better than the author can" scenarios.
The demo in question is coming soon, and will be a long-overdue update to my Adaptive User Experiences in IBM XPages Applications series.
In Closing...
So scriptBlock artifacting is the practice of using an xp:scriptBlock Control in XPages to create Client Side JavaScript variables or functions for use throughout your XPages applications. It's something that I find myself using the further I dive into using Dojo ContentPane controls how they're intended to be used (for example, content population via XHR) vs. how a traditional XPages Developer would use them.
Questions or comments below, and Happy Coding!