dominoGuru.com

Latest Updates

Products & Applications

Showtime
My Blackberry Enterprise Server Push Utility for the Lotus Notes Client, allows you to create Jobs for individual Channel, Message, and Browser Content Pushes, as well as allows you to delete Pushed Channel Icons from defined recipient devices.

Time Tracker
The idea is simple. At the start of your day - upon completion of your first task - create an entry highlighting what you did and whether you feel it was an efficient or inefficient use of your time. Based on several requests, you can also select the priority, apply categories, or even align your time against a project.

For Lotus Notes Client v8.0 and above, you can use the Time Tracker Widget to make this process even easier!

Zephyr
My Configuration-based Rich Text Mail Merge and Emailing Utility, Zephyr allows you to create rich, data-driven emails to support automated workflow - all via Microsoft Word Mail Merge-like architecture. Dear <firstname> allows you to personalize each email message not only to the individual recipient, but also to the individual application workflow event!

xCopy
xCopy is a simple configurable xCopy client for the Lotus Notes client. By creating and defining xCopy Profiles, you can batch process your file backup or remote upload jobs. With the addition of the xCopy sidebar widget, you can easily kick-off these jobs, and modify both the xCopy Profiles and xCopy itself.

Community & Resources

Lotus Technical Information & Education Community

The Lotus Technical Information & Education community is comprised of IBM, business partner, and customer subject matter experts who use product wikis, published articles, white papers, community blogs and the latest in social media to build and share high quality technical content.

OpenNTF.org - Open Source Community for Lotus Notes Domino

OpenNTF is devoted to enabling groups of individuals all over the world to collaborate on IBM Lotus Notes/Domino applications and release them as open source.

developerWorks Lotus : Wikis

Share your deployment experiences and best practices in our wikis and help IBM to create scenarios for successful deployments. Contribute to the community by collaborating on shared content and leverage the shared knowledge from that community.

Using Custom Properties with XPages Custom Controls

08/29/2011 12:26:00 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: 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!

 
Atul SaxenaName:Atul SaxenaComment Dear Christopher,

This is excellent one.

Best Regards

Atul Saxena

(not published)




Evaluate this Formula: @LowerCase(@Text("FOO"))