dominoGuru.com

Your Development & Design Resource

Using Custom Properties with XPages Custom Controls

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: Apple.com

Horizontal Tab Navigation 
Example: Google.com' Horizontal Tab Navigation Example: Google.com

Horizontal Tab Navigation 
Example: Expedia.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 '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 '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' 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' 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 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 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!


More from dominoGuru.com


dominoGuru.com is powered by IBM Notes Domino XPages & hosted by Prominic.NET

Contact Us

Use our Contact / Feedback form or one of these email addresses:

Creative Commons License

Except where otherwise noted, dominoGuru.com by Chris Toohey is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License.