Apr 7, 2011

Getting / Setting Values with SSJS and CSJS

Below are some methods we have come across for getting and setting values in XPages. This list is an ongoing process. Please comment if you see something that isn't listed.

SSJS - Server Side JavaScript

Getting SSJS Values (getting values with SSJS require the data to be submitted. These snippets will return 'null' if the data has not been updated/submitted to the server)
------------------------------------ 
Edit Box, Check Box, Radio Button, Radio Button Group:
getComponent("ControlID").value

Check Box group:
...


Setting SSJS Values
------------------------------------
Edit Box:
getComponent("ControlID").setValue("something");


______________________________________________


CSJS - Client Side JavaScript

Getting CSJS Values
------------------------------------
Edit Box, Combo Box, Radio Button:
XSP.getElementById("#{id:ControlID}").value

Check Box:
XSP.getElementById("#{id:ControlID}").checked  //will return 'true' if checked, 'false' if not checked

Check Box Group:
var checkField=document.getElementsByName("#{id: ControlID }");
var result=[];
for(var i=0;i<checkField.length;i++){
if(checkField[i].checked){
result.push(checkField[i].value);
}
}
alert(result.join(','));

Radio Button Group:
var result=null;
for(var i=0; i<document.forms[0].elements.length; i++){
    if(document.forms[0].elements[i].name=="#{id:ControlID}" ){

        if(document.forms[0].elements[i].checked == true){
            result=document.forms[0].elements[i].value;
            break; //remove this if for check box groups and collect multiple values above instead
}}}
alert(result); //result is the currently selected value of the radio group


Setting CSJS Values
-----------------
Edit Box:
XSP.getElementById("#{id:ControlID}").value = "something";

Radio Button Group:
function setRadioValue(id, value)
{
    var elements = document.getElementsByName (id);
    for(i=0;i<elements.length;i++) {
        if (elements[i].value == value) {
            elements[i].checked = true;
        }
    }
}
setRadioValue("#{id:radioGroup}", "value");

Mar 1, 2011

Computed Tab Layout in a Custom Control

Our goal is to create a tab layout that can be updated dynamically from a configuration document.  We found two ways this can be accomplished, one is with a repeat control and the other is with property definitions.  The two different ways are explained below:

Overview
On our main Xpage, we have fields to get the data from the configuration document based on the revision date we want to use.  There is a document for each revision date.  This document contains a list of tab names and the custom controls to be used as the tabs content. 

Tab Names : 
Insured, Locations, Quote Summary, Application Questions, Application Information, Submission

Tabs (custom controls): 
tab_Insured_20110301.xsp, tab_Locations_20110101.xsp, tab_QuoteSummary_20110101.xsp, tab_ApplicationQuestions_20110101.xsp, tab_ApplicationInfo_20110101.xsp, tab_Submission_20110101.xsp

@DbLookup("", "RevisionDateConfigDocs", getComponent("RevisionDate").getValue(), "ElementListTabNames")

Next we have a custom control for the tab layout.  We have a Tabbed Panel with 10 tabs.  10 is our default, however we may not have that many tabs so we added logic to the rendered property to only show the tabs we need.     

Repeat Control
For each Tab Panel, the label is set to a value from the field we set on the main page.   A repeat control is added to the panel for the field with the list of custom control names.  The option “Create controls at page creation” needs to be marked so this doesn’t get created before our fields are set.  We set the starting index to the current tab (example:  first tab is 0, second tab is 1) and the repeat limit is always set to 1.  Inside each repeat control we have an Include Page container.  The page name is set to the repeat controls collection name.  

Here’s the logic for the first two tabs:

<xp:tabbedPanel id="Tabs" loaded="true">
   <xp:tabPanel id="tab0">            
      <xp:this.label>
<![CDATA[#{javascript:getComponent("RevisionTabNames").getValue()[0]}]]>
</xp:this.label> 
<xp:this.rendered>
<![CDATA[#{javascript:var tabs=getComponent("RevisionTabs").getValue()
if(tabs.length<1)return false
return true}]]>
</xp:this.rendered>
<xp:repeat id="repeat1" rows="1" var="tabData" repeatControls="true" first="0">                                     
<xp:this.value>
<![CDATA[#{javascript:getComponent("RevisionTabs").getValue()}]]></xp:this.value> 
<xp:include id="include0" pageName="${javascript:tabData}">           </xp:include>
      </xp:repeat>           
   </xp:tabPanel>

<xp:tabPanel id="tab1">             
     <xp:this.label>
        <![CDATA[#{javascript:getComponent("RevisionTabNames").getValue()[1]}]]>
      </xp:this.label>
<xp:this.rendered>
<![CDATA[#{javascript:var tabs=getComponent("RevisionTabs").getValue()
if(tabs.length<2)return false
return true}]]>
</xp:this.rendered>
<xp:repeat id="repeat2" rows="1" var="tabData" repeatControls="true" first="1">                                     
<xp:this.value>
<![CDATA[#{javascript:getComponent("RevisionTabs").getValue()}]]></xp:this.value>
            <xp:include id="include1" pageName="${javascript:tabData}">
</xp:include>
      </xp:repeat>           
</xp:tabPanel>
</xp:tabbedPanel>

Property Definitions
For each Tab Panel, the label is set to the names property we set before the page loads.  Inside each Tab Panel we have an Include Page container.  The page name is set to the pages property. 
      
Tabs.ssjs – function is called from the beforePageLoad event to set these properties.
function initTabs(){
var tNames = @DbLookup("", "RevisionDateConfigDocs",  getComponent("RevisionDate").getValue(), "ElementListTabNames");

var tRevision = @DbLookup("", "RevisionDateConfigDocs", getComponent("RevisionDate").getValue(),"ElementListTabs");

     
      for(var i in tNames){
            compositeData.Tabs.names[i] = tNames[i];
      }
     
      for(var i in tRevision){
            compositeData.Tabs.pages[i] = tRevision[i];
      }

compositeData.numTabs = tNames.length;

}

Here’s the logic for the first two tabs:
<xp:view xmlns:xp="http://www.ibm.com/xsp/core"
      xmlns:xc="http://www.ibm.com/xsp/custom"
      beforePageLoad="#{javascript:initTabs();}">
<xp:this.resources>
      <xp:script src="/Tabs.jss" clientSide="false"></xp:script>
</xp:this.resources>

<xp:tabbedPanel id="Tabs" loaded="true">

      <xp:tabPanel id="tab0">       
      <xp:this.label><![CDATA[#{javascript:compositeData.Tabs.names[0];}]]>
</xp:this.label>
      <xp:this.rendered>
      <![CDATA[#{javascript:if(compositeData.numTabs<1)return false 
         return true}]]>
      </xp:this.rendered>
      <xp:include pageName="${javascript:compositeData.Tabs.pages[0]}" id="include1"></xp:include>
</xp:tabPanel>

<xp:tabPanel id="tab1">
      <xp:this.label><![CDATA[#{javascript:compositeData.Tabs.names[1]}]]>
</xp:this.label>
      <xp:this.rendered>
      <![CDATA[#{javascript:if(compositeData.numTabs<2)return false
        return true}]]>
      </xp:this.rendered>
      <xp:include pageName="${javascript:compositeData.Tabs.pages[1]}" id="include2"></xp:include>
</xp:tabPanel>
</xp:tabbedPanel>
</xp:view>

Logic on the main xpage for the custom control and custom properties:
      <xc:cc_Tabs>
            <xc:this.Tabs>
                  <xc:Tabs>
                        <xc:this.names>
                              <xc:names></xc:names>
                        </xc:this.names>
                        <xc:this.pages>
                              <xc:pages></xc:pages>
                        </xc:this.pages>
                  </xc:Tabs>
            </xc:this.Tabs>
      </xc:cc_Tabs>

The Results

Summary
As you can see, there are at least two different ways to do the same thing.  Which one is better; repeat control or property definitions?  Or is there an even better way?  We would love your feedback on this subject.

Feb 24, 2011

Setting constraints on a Dojo control

Constraints in Dojo are objects passed to functions responsible for validating, parsing, and formatting the data in the box, and various properties may be provided to override system or locale-specific defaults.

I was having difficulty finding an example used in XPages. Code below is a constraint used on a dijit.form.NumberTextBox. I'm just setting the number pattern to accept a number with a comma. You can setup multiple constraints...just separate with semi-colons.

<xp:inputText value="#{document.Number}" id="number1" dojoType="dijit.form.NumberTextBox">
    <xp:this.dojoAttributes>
        <xp:dojoAttribute name="constraints" value="{pattern: '#,##0'}"></xp:dojoAttribute>
    </xp:this.dojoAttributes>
</xp:inputText>

Feb 17, 2011

Dojo tab container code

<?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" dojoTheme="true" dojoParseOnLoad="true">

    <script type="text/javascript">
        dojo.require("dijit.layout.ContentPane");
        dojo.require("dijit.layout.TabContainer");
    </script>

    <h1>Tab Container</h1>
    <div id="mainTabContainer" dojoType="dijit.layout.TabContainer"
        style="width:900px;height:500px">
        <div id="tab1" dojoType="dijit.layout.ContentPane"
            title="First Tab" selected="true">
            First Tab
        </div>
        <div id="tab2" dojoType="dijit.layout.ContentPane"
            title="Second Tab">
            Second Tab
        </div>
        <div id="tab3" dojoType="dijit.layout.ContentPane"
            title="Third Tab">
            Third Tab
        </div>
    </div>
</xp:view>

Feb 8, 2011

Excellent XPages Resources

Here is a list of links that will be updated periodically with helpful XPages resources.

Xpages.info
Planet Lotus
XPages: Tutorials
Learning XPages by Declan Lynch
Wissel.net
XPages Blog
Notes In 9
DontPanic

Embracing XPages

<xp:label value="Hello World."></xp:label>