dominoGuru.com
Your Development & Design Resource
Using Custom Properties with XPages Custom Controls
08/29/2011 12:26 PM by Chris Toohey
Introduction
I have in the past often potentially trivialized Custom Controls in XPages by describing them as "like Subforms" when attempting to find their corollary Traditional Design Element. And while a Custom Control can contain a Subform-like subsection of a given XPages Form, their application and usefulness far exceed that of "here's a common group of Fields or Form Actions".
This article will hopefully illustrate the potential of how useful they can be and inspire you to create your own Custom Controls by showing you how to employ Custom Properties in your Custom Controls.
Build the Demo
At the end of this article, you will have a fully-customizable horizontal tab navigation Custom Control.
Y'know... the kind that use a combination of Unordered List (UL), List Item (LI), and Anchor (A) tags with some CSS to created the tabbed table-style navigation that we've all seen before.
Horizontal Tab Navigation Example: Apple.com
Horizontal Tab Navigation Example: Google.com
Horizontal Tab Navigation Example: Expedia.com
You get the idea.
To start, we'll create a blank Custom Control in our Demo NotesDatabase named "navigator.xsp". The "navigator.xsp" will use a combination of pass-thru markup and core XPage Controls.
Once you've created the Custom Control, we will next add two Custom
Properties: tabs
and active
.
In the Custom Control Properties, you will find the Property Definitions tab (see examples below). From there, we can add the two Custom Properties.
'navigation.xsp' Control Custom Property: tabs
The tabs Property will actually accept an object (in the
case of this demo, via an Array generated from the @GetTabs()
SSJS
Function).
Example tabs Syntax:
return [
{
label:
'Home',
href:
'Home.xsp',
handle:
'home'
},
{
label:
'Tab 1',
href:
'Tab1.xsp',
handle:
'tab1'
},
{
label:
'Tab 2',
href:
'Tab2.xsp',
handle:
'tab2'
}
];
'navigation.xsp' Control Custom Property: active
Example active Syntax... well, it's really just a String. So "foo" is a valid, but it's worth noting that the intent for the active Custom Property is to match up with the "handle" from the tabs Custom Property. To clarify, the active Property will be based within the HTML-generating ComputedText Control to conditionally set an active Class Attribute to an HTML Anchor Element.
To use these Custom Properties, we will use the verb/handle
compositeData
to refer to the Custom Control, and then the name of
the given Custom Property.
To see this in action (so it will hopefully make any bit of sense...), check out the XPage markup for "navigator.xsp".
navigator.xsp Custom Control
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
<ul class="navigation">
<xp:repeat value="#{javascript:compositeData.tabs}" var="tab">
<xp:text tagName="li" escape="false" styleClass="">
<xp:this.value><![CDATA[#{javascript:var isActive:string;
if (compositeData.active == tab.handle) {
isActive = '
class="active"';
} else {
isActive = '';
}
return '<a href="' + tab.href + '"' + isActive +
'>' + tab.label + '</a>';}]]></xp:this.value>
</xp:text>
</xp:repeat>
</ul>
</xp:view>
Since the tabs Custom Property will be an object (again, in this
case an Array), I can use it as the value of a Repeat Control within my Custom
Control. Using the defined var "tab", I can use the extended Array
navigation syntax and simply grab any of the values from said Array item that I
need. (eg., tab.label
or tab.href
).
At this point, I'll show you the @GetTabs()
SSJS Function... or
the next part might get a little confusing...
"@GetTabs()" SSJS Function
var @GetTabs=function(key:string) {
switch(key) {
case 'alternate':
return [
{
label: 'Home',
href: 'Home.xsp',
handle: 'home'
},
{
label: 'Tab
A',
href: 'TabA.xsp',
handle: 'taba'
},
{
label: 'Tab
B',
href: 'TabB.xsp',
handle: 'tabb'
}
];
break;
default:
return [
{
label: 'Home',
href: 'Home.xsp',
handle: 'home'
},
{
label: 'Tab
1',
href: 'Tab1.xsp',
handle: 'tab1'
},
{
label: 'Tab
2',
href: 'Tab2.xsp',
handle: 'tab2'
}
];
break;
}
}
After including the SSJS Script Library containing this function and adding two of the navigator Custom Controls to our Demo.xsp XPage (along with some header-style text to help illustrate the demo), we get the following XPage:
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core" xmlns:xc="http://www.ibm.com/xsp/custom">
<xp:this.resources>
<xp:script src="/com.dominoguru.core.jss" clientSide="false" />
<xp:styleSheet href="/screen.css" />
</xp:this.resources>
<xp:paragraph styleClass="">
<strong>Default Navigation</strong>
</xp:paragraph>
<xc:navigator active="home" tabs="#{javascript:@GetTabs()
}" />
<xp:paragraph styleClass="">
<strong>Alternate Navigation:
@GetTabs('alternate')</strong>
</xp:paragraph>
<xc:navigator active="" tabs="#{javascript:@GetTabs('alternate')}" />
</xp:view>
This gives us two unordered lists that contain links based off of the list
arrays we wrote in the @GetTabs()
SSJS function.
XPages Custom Control Properties Demo - No Style Unordered List 'Navigators'
If we add the following CSS:
ul.navigation {
display:
inline-block;
width: 100%;
margin: 25px 0px 25px 0px;
padding: 0px 0px 0px 15px;
list-style:
none;
border-bottom:
1px solid #666;
}
ul.navigation li {
display: inline;
margin: 0px;
padding: 0px;
list-style:
none;
}
ul.navigation li a {
display:
inline-block;
width: 100px;
padding: 2px 10px 4px 10px;
margin: 0px 2px 0px 0px;
color: #333;
border: 1px solid #666;
background-color: #999;
font-weight:
normal;
border-radius: 10px 10px 0px 0px;
-moz-border-radius: 10px 10px 0px 0px;
-webkit-border-bottom-right-radius:
0px;
-webkit-border-bottom-left-radius: 0px;
position: relative;
bottom: -1px;
}
ul.navigation li a:hover
{
text-decoration: none;
background-color: #356AA0;
color: #eee;
padding-top:
4px;
font-weight:
bold;
}
ul.navigation li a.active, ul.navigation li a.active:hover {
border-bottom:
1px solid #fff;
background-color: #fff;
padding-top:
4px;
font-weight:
bold;
color: #000;
}
We'll have our same two navigators, but this time looking and acting like the typical horizontal tab navigation we've seen on countless websites and applications:
XPages Custom Control Properties Demo - Styled Unordered List 'Navigators'
And there we have it, a simple, flexible, and reusable Custom Control to handle Horizontal Navigation. Of course, we can really trick this demo out.
"Home.xsp" XPage
<?xml version="1.0" encoding="UTF-8"?>
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
xmlns:xc="http://www.ibm.com/xsp/custom">
<xp:this.resources>
<xp:script src="/com.dominoguru.core.jss" clientSide="false" />
<xp:styleSheet href="/screen.css" />
</xp:this.resources>
<xp:div styleClass="container_main">
<xp:div id="container_header" styleClass="container_header">
<xp:div styleClass="navigator_main">
<xc:navigator
tabs="#{javascript:@GetTabs('navigator_main')}" active="home">
</xc:navigator>
</xp:div>
<xp:div id="navigator_actionbar" styleClass="navigator_actionbar">
<xc:navigator
tabs="#{javascript:@GetTabs('navigator_actionbar')
}">
</xc:navigator>
</xp:div>
</xp:div>
<xp:div styleClass="container_body">
<xp:div id="column_left" styleClass="column_left">
<xc:navigator
tabs="#{javascript:@GetTabs('column_left')}" active="">
</xc:navigator>
</xp:div>
<xp:div styleClass="column_body">
<xp:paragraph>This is
the Home Page!</xp:paragraph>
</xp:div>
</xp:div>
<xp:div id="container_footer" styleClass="container_footer">
<xp:div styleClass="navigator_main">
<xc:navigator
tabs="#{javascript:@GetTabs('navigator_main')}" active="home">
</xc:navigator>
</xp:div>
</xp:div>
</xp:div>
</xp:view>
Then we add some new tab options in the @GetTabs()
functions:
...
case 'navigator_main':
return [
{
label:
'Home',
href:
'Home.xsp',
handle:
'home'
},
{
label:
'Mail',
href:
'Home.xsp',
handle:
'taba'
},
{
label:
'Calendar',
href:
'Home.xsp',
handle:
'tabb'
},
{
label:
'Documents',
href:
'Home.xsp',
handle:
'tabb'
},
{
label:
'Contacts',
href:
'Home.xsp',
handle:
'tabb'
}
];
break;
case 'navigator_actionbar':
return [
{
label:
'New Mail',
href:
'Home.xsp',
handle:
'newmail'
},
{
label:
'New Calendar Entry',
href:
'Home.xsp',
handle:
'newcalendar'
},
{
label:
'New Document',
href:
'Home.xsp',
handle:
'newdocument'
},
{
label:
'New Contact',
href:
'Home.xsp',
handle:
'newcontact'
}
];
break;
case 'column_left':
return [
{
label:
'<img src="blogger.png"
/>',
href:
'http://www.dominoguru.com',
handle:
'site'
},
{
label:
'<img src="facebook.png"
/>',
href:
'http://www.facebook.com/christoohey',
handle:
'facebook'
},
{
label:
'<img src="twitter.png"
/>',
href:
'http://www.twitter.com/christoohey',
handle:
'twitter'
},
{
label:
'<img src="googleplus.png"
/>',
href:
'https://plus.google.com/106322798397736404208',
handle:
'googleplus'
},
{
label:
'<img src="linkedin.png"
/>',
href:
'http://www.linkedin.com/profile/view?id=4325776',
handle:
'linkedin'
},
{
label:
'<img src="youtube.png"
/>',
href:
'http://www.youtube.com/christoohey',
handle:
'youtube'
}
];
break;
...
Now, without any applied stylesheet, you would get the following:
Home.xsp Demo XPage with multiple Custom Controls - No Style
And with a little specifically-targeted style:
Styled Home.xsp Demo XPage with multiple Custom Controls
Drastically different, and amazingly simple to maintain. Of course, this is only the beginning. I can load additional Custom Properties into the "navigator.xsp" Custom Control... but I've already made this article near whitepaper-length.
Conclusion
For those curious, you can check out the Home.xsp Demo online... but the entire build is in this article. Yes, I know, none of the links actually work (they all point to Home.xsp)... but I'm lazy.
Ideally, I'd have a few pages that illustrate setting the
active
Properties via URL Query Strings, other SSJS Functions,
various Control Value states (ie., if the "section" dropdown equals
"Documents", then set the "Documents" tab as "active"...).
In the case of this demo, for the two "active" definitions, I simply used the name of the Home.xsp handle ("home"). While you might want to employ a more dynamic approach, you can always just manually set them per XPage that you're on. See, ya got options!
You really start to get a sense for just how powerful and flexible XPages Custom Controls can be as this article has really only slightly covered the ability to add Custom Properties to a given Control.
As always, if anyone has any questions or feedback, let me know via the comments... and thanks for reading this far!