Using Zen Components
Navigation Components
[Back] [Next]
   
Server:docs2
Instance:LATEST
User:UnknownUser
 
-
Go to:
Search:    

The Zen Layout chapter of Using Zen introduces Zen group components as the key to laying out the Zen page. In that chapter, the Groups section describes the simple group components <hgroup>, <vgroup>, <page>, <pane>, and <spacer>.

This chapter describes a more complex set of group and menu components. A developer can use these components to support navigation through Zen applications:
The following table organizes Zen navigation components into two categories: containers (at left) and contained (at right). This chapter describes each component in the order listed in the table.
Container Purpose Components It Typically Contains
(Any) Provide a link to another page or to a popup message. <link> for each link.
<locatorBar> Navigation bar for the top of the page. <locatorLink> for each link in the navigation sequence.
<menu> Menu container. The default layout is vertical but you can use the layout attribute to change to horizontal layout. Alternatively, you may use <hmenu> or <vmenu>.
<menuItem> for each option.
<menuSeparator> for the space between options.
<hmenu> Horizontally oriented menu
<vmenu> Vertically oriented menu
<tabGroup> Traditional set of tabs from which the user can choose one to be displayed on top of the set. <tab> for each option.
<lookoutMenu> A tabbed menu that displays a button for each tab. Clicking on a button makes the tab contents display beneath the button. Each tab consists of menu items.
<tab> A tab is a group that may contain any combination of components. Inside a <lookoutMenu>, each <tab> contains <menuItem> and <menuSeparator> components.
<expando> Expanding and contracting group which may contain any combination of components. For a simple navigation tree, you can provide <link> components within <expando>.
<dynaTree> Expandable tree of links. Generated links. These either use the data within the global or are supplied by the callback. <dynaTree> is not a group and does not contain other components.
<buttonView> Set of links that permits filtering according to categories. Buttons laid out in a grid according to the number of columns specified. <buttonView> is not a group and does not contain other components.
The following figure lists most of the components described in this chapter. All of the classes shown in the diagram are in the package %ZEN.Component, for example %ZEN.Component.tab. The diagram shows the inheritance relationships for these classes. It also highlights which of these components can contain other components, by showing which components inherit from %ZEN.Component.group.
Zen Navigation Components
Links
The following Zen components provide links to content that is available via a URI:
<link>
The <link> component outputs a simple link (an HTML anchor element) to the Zen page. The default formatting for this type of link is to use color and underline it. <link> has the following attributes:
Attribute Description
Zen component attributes
<link> has the same general-purpose attributes as any Zen component. For descriptions, see these sections:
caption
Text for this link in the Zen page display.
Although you can enter ordinary text for this attribute, it has the underlying data type %ZEN.Datatype.caption. See Zen Attribute Data Types.”
The caption value can be a literal string, or it can contain a Zen #()# runtime expression.
disabled
If true, this link is disabled. The default is false. A newly disabled link is redisplayed, without an anchor tag, to ensure that it is truly disabled from the user’s point of view.
This attribute has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
href
URI of the content to display when the user clicks the caption text. If you want to invoke a client-side JavaScript method in the href, start the URI with javascript: as in:
The href value can be a literal string, or it can contain a Zen #()# runtime expression.
onclick
The onclick event handler for the link. Zen invokes this handler when the user clicks on the link. See Zen Component Event Handlers.” If onclick is specified, href is ignored. If onclick is not specified, the link displays the content specified by href.
style
CSS style to apply to cells in this link, for example:
target
String that controls where the new document is displayed when the user clicks on a link. In HTML, this is typically the name of a frame; however HTML also defines the following special values, which you can assign to the target attribute to get the desired behavior:
  • "_blank" — Open the link in a new window
  • "_parent" — Open the link in the parent window
  • "_self" — Open the link in the current window
  • "_top" — Open the link in the topmost window
title
Help message to display when the user hovers the cursor over the link, without clicking.
Although you can enter ordinary text for this attribute, it has the underlying data type %ZEN.Datatype.caption. See Zen Attribute Data Types.”
The title value can be a literal string, or it can contain a Zen #()# runtime expression.
One way to use <link> is to place it within an <expando> component to present a tree-style menu of links.
<locatorBar>
A <locatorBar> looks like the following example, based on the class ZENDemo.ActiveGroupDemo in the SAMPLES namespace. The <locatorBar> is the horizontal bar along the top of the illustration.
The corresponding <locatorBar> definition follows:
<locatorBar id="locator" OnGetQuickLinks="GetQuickLinks">
  <locatorLink caption="Home" title="Home page"
               href="ZENDemo.Home.cls"/>
  <locatorLink caption="Active Group Demo"
               title="Active Group Demo" />
</locatorBar>
This <locatorBar> definition contains two <locatorLink> entries, which display in sequential order beginning at the far left end of the bar:
[Home] > [Active Group Demo]
The caption for each <locatorLink> is enclosed in square brackets and is separated by a right angle bracket from the next <locatorLink> in the sequence, from left to right. The user can display the title as a tooltip.
At the far right end of the bar is the drop-down list of quick links that this <locatorBar> has established by identifying an OnGetQuickLinks callback. The illustration shows that the user is hovering the cursor over this list and is about to make a selection. Doing so would cause the page identified by that link to display.
<locatorBar> has the following attributes:
Attribute Description
Zen component attributes
<locatorBar> has the same general-purpose attributes as any Zen component. For descriptions, see these sections:
OnDrawBar
Name of a server-side callback method in the Zen page class. This method provides HTML content for the locator bar using &html<> syntax or WRITE commands.
Zen invokes this method whenever it draws the locator bar, automatically passing it a %String that contains the seed value from the <locatorBar>. The callback must return a %Status data type. The following is a valid method signature:
To use the above method as the callback, the developer would set OnDrawBar="DrawBar" for the <locatorBar>.
OnGetQuickLinks Name of a server-side callback method in the Zen page class. This method defines a set of quick links to appear as a Go to drop-down list at the top right of the locator bar. For details, see the discussion following this table.
seed Allows you to pass some arbitrary value to the OnDrawBar callback. The seed value can be a literal string, or it can contain a Zen #()# runtime expression.
The OnGetQuickLinks attribute provides the name of a server-side callback method in the Zen page class. This method must fill the array that is provided to it. If the method constructs a valid array, Zen displays each entry in the array as a Go to drop-down list at the top right of the locator bar. Zen invokes this method whenever it draws the locator bar, automatically passing in its single output parameter, an array subscripted by link caption. The callback must return a %Status value.
The following sample ObjectScript statement provides one entry for the array:
Set pLink("caption")="uri"
Where:
The following example shows a valid method signature and use of parameters. It generates the quick links shown in the illustration at the beginning of this topic. To use the following method as the callback, the developer would set OnGetQuickLinks="GetLinks" for the <locatorBar>.
ClassMethod GetQuickLinks(Output pLinks) As %Status
{
  Set pLinks("Home") = "ZENDemo.Home.cls"
  Set pLinks("Expense Calculator") = "ZENDemo.ExpenseCalculator.cls"
  Set pLinks("MVC Master Detail") = "ZENMVC.MVCMasterDetail.cls"
  Set pLinks("MVC Chart") = "ZENMVC.MVCChart.cls"
  Set pLinks("MVC Meters") = "ZENMVC.MVCMeters.cls"
  Set pLinks("MVC Form") = "ZENMVC.MVCForm.cls"
  Set pLinks("Test Suite") = "ZENTest.HomePage.cls"
  Set pLinks("Controls") = "ZENDemo.ControlTest.cls"
  Set pLinks("Methods") = "ZENDemo.MethodTest.cls"
  Quit $$$OK
}
If you want to define the same set of quick links to use on more than one page in your application, you can define a method in your Zen application class and then have the callback in each page class invoke this application method, as follows. Note the dot (.) in front of the parameter pLinks in the call to the application class method. This is because it is an output parameter:
ClassMethod GetQuickLinks(Output pLinks) As %Status
{
  #; dispatch to our application class
  Quit %application.GetQuickLinks(.pLinks)
}
<locatorLink>
<locatorLink> can appear only inside the <locatorBar> container. Each <locatorLink> defines one link within the navigation chain expressed in the <locatorBar>. <locatorLink> is the XML projection of the auxiliary class %ZEN.Auxiliary.locatorLink. <locatorLink> has the following attributes:
Attribute Description
id This value can be used to select a CSS style definition for the <locatorLink>.
name Component name. Typically, this is not used for <locatorLink>.
caption
Text for this link in the <locatorBar>.
Although you can enter ordinary text for this attribute, it has the underlying data type %ZEN.Datatype.caption. See Zen Attribute Data Types.”
href
URI of the content to display when the user clicks the caption text. If you want to invoke a client-side JavaScript method in the href, start the URI with javascript: as in:
title
Help message to display when the user hovers the cursor over the link, without clicking.
Although you can enter ordinary text for this attribute, it has the underlying data type %ZEN.Datatype.caption. See Zen Attribute Data Types.”
Menus
Menu components permit you to create classic navigation menus, with lists of choices from which the user can select an item by clicking on it. With each choice of a menu item, an event may occur depending on how the menu and menu item have been defined:
A vertical menu, expanded to three levels, looks like the following example, based on the class ZENTest.MenuTest in the SAMPLES namespace. Any options that produce submenus use the » right angle quote to indicate this, as shown for “Submenu” and “Sub Submenu”.
The <menu> definition that produced this illustration is shown below:
<menu id="menu2" layout="vertical">
  <menuItem caption="Menu A" link="javascript: alert('A');" />
  <menuItem caption="Menu B" link="javascript: alert('B');" />
  <menu id="menu2B" caption="Submenu" layout="vertical"
        onactivate="zenPage.activateMenu2B();">
    <menuItem caption="Menu A" link="javascript: alert('A');" id="menu2B_A" />
    <menuItem caption="Menu B" link="javascript: alert('B');" />
    <menu id="menu2BB" caption="Sub Submenu"  layout="vertical">
      <menuItem caption="Menu A" link="javascript: alert('A');" />
      <menuItem caption="Menu B" link="javascript: alert('B');" />
      <menuSeparator />
      <menuItem caption="Help Desk" link="ZENApp.HelpDesk.cls" />
    </menu>
  </menu>
  <menuSeparator />
  <menuItem caption="Test Suite" link="ZENTest.HomePage.cls" />
  <menuItem caption="Home" link="ZENDemo.Home.cls" />
</menu>
The next several sections explain details of the menu components in this example. For now, simply note:
Note:
The Client Side Menu Components chapter in Developing Zen Applications describes more sophisticated menu components that are more easily fine-tuned and customized. Try the components in this chapter first, and if they do not suit your needs, consult the other book.
<menuItem>
Each item on a <menu> is defined using <menuItem>. <menuItem> has the following attributes. Since either of them may display their caption or image as a choice within a menu cell, <menuItem> and <menu> share all of these attributes in common. <menu> has additional attributes. For descriptions, see the <menu> section.
Menu Cell Attributes
Attribute Description
Zen component attributes
<menu> and <menuItem> have the same general-purpose attributes as any Zen component. For descriptions, see these sections:
caption
Text for this menu item when it appears as a choice within its containing <menu>.
Although you can enter ordinary text for this attribute, it has the underlying data type %ZEN.Datatype.caption. See Zen Attribute Data Types.”
disabled
If true, this menu item is disabled. The default is false.
This attribute has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
help
The help attribute supplies help text for this menu item.
Generally what you want in place of help is the title attribute. title supplies tooltip text that displays whenever the user hovers the mouse over this item. All Zen components, including <menu> and <menuItem>, support the title attribute.
Although you can enter ordinary text for this attribute, it has the underlying data type %ZEN.Datatype.caption. See Zen Attribute Data Types.”
image
Identifies the pathname and filename of an image to display for this menu item. If both image and caption are provided, they appear side by side, the image at left and the text at right.
The image path is relative to the Caché installation directory. Typically this path identifies the images subdirectory for your Zen application, for example:
imageHeight If an image is provided, imageHeight is its height in pixels. The default is 16.
imageWidth If an image is provided, imageWidth is its width in pixels. The default is 16.
link
URI of the content to display when the user clicks the menu item. If you want to invoke a client-side JavaScript method in the link, start the URI with javascript: as in:
linkResource Name of a Caché resource. The user must have privileges to access this resource or this menu item becomes disabled. If you are not familiar with Caché resources, see the Assets and Resources chapter in the Caché Security Administration Guide.
onclick
The onclick event handler for the menu item. Zen invokes this handler when the user clicks on the menu item. See Zen Component Event Handlers.” If onclick is specified, link is ignored. If onclick is not specified, the menu item displays the content specified by link.
target
String that controls where the new document is displayed when the user clicks on a menu item. In HTML, this is typically the name of a frame; however HTML also defines the following special values, which you can assign to the target attribute to get the desired behavior:
  • "_blank" — Open the document in a new window
  • "_parent" — Open the document in the parent window
  • "_self" — Open the document in the current window
  • "_top" — Open document link in the topmost window
<menu>, <hmenu>, and <vmenu>
All Zen groups that do not have a predefined layout setting have a vertical layout by default. This is true for the <menu> component. However, it can be useful to explicitly state the desired layout of the menu group. You can do this by:
<menu>, <hmenu>, and <vmenu> have the following attributes:
Attribute Description
Zen group attributes A menu has the same style and layout attributes as any Zen group. For descriptions, see Group Layout and Style Attributes in the “Zen Layout” chapter of Using Zen. However, the layout attribute does not apply to <hmenu> or <vmenu> because these components have layout set to "horizontal" and "vertical" respectively.
Zen menu cell attributes A menu may display its caption or image as a choice within a menu cell. For this reason, <menu>, <hmenu>, and <vmenu> share several attributes in common with <menuItem>. For descriptions, see the <menuItem> section.
onactivate
The onactivate event handler for the menu. See Zen Component Event Handlers.” Used if this menu is a submenu. Zen invokes this handler just before the submenu is made visible.
onshowHelp Client-side JavaScript expression that Zen invokes when the user moves the mouse over this menu item. Generally this expression invokes a client-side JavaScript method.
<menuSeparator>
A <menuSeparator> bar helps to visually group the options on a <menu>. <menuSeparator> has the same general-purpose attributes as any Zen component. For descriptions, see these sections:
<accordionMenu>
An <accordionMenu> is a simple HTML5 accordion menu component. This component runs correctly only on HTML5 compliant browsers. <accordionMenu> supports the following attributes:
Attribute Description
ongetdata The ongetdata event handler. If it is defined, this event returns an array of items to be displayed in the menu.
onselect The onselect event handler. If it is defined, this event is fired when the user clicks on an item in the menu.
selectedIndex The currently selected item. This is a string of the form 'index1,index2,index3', where each index is the 0-based ordinal position of a menu, and its first and second level children. <accordionMenu> does not support more than three levels.
style An additional style to apply to items in the menu.
You can get data to populate the <accordionMenu> either from an ongetdata event handler, or by using the controllerId property to connect the menu to an <altJSONProvider>. The following code samples show a simple <accordionMenu> that uses an <altJSONProvider> which gets data from an OnGetArray callback method.
<page xmlns="http://www.intersystems.com/zen" title="">
  <altJSONProvider id="jsonP" OnGetArray="GetData"/>
  <accordionMenu
    id="testMenu" 
    selectedIndex="1,2" 
    controllerId="jsonP" 
    width="200"
    onselect="zen('sid').setProperty('value',key);"
  />
  <text id="sid" size="20"/>
</page>
Method GetData(ByRef pParameters, Output pMetaData, Output pData) As %Status
{
  Set pMetaData = $LB("key","caption","image")
  
  Set pData(0) = $LB("1","Menu A","images/folderclosed.gif")
    Set pData(0,1) = $LB("0,1","Selection 1","images/file.png")
    Set pData(0,2) = $LB("0,2","Selection 2","images/file.png")
    Set pData(0,3) = $LB("0,3","Selection 3","images/file.png")
  Set pData(1) = $LB("1","Menu B","images/folderclosed.gif")
    Set pData(1,1) = $LB("1,1","Choice 1","images/file.png")
    Set pData(1,2) = $LB("1,2","Choice 2","images/file.png")
    Set pData(1,3) = $LB("1,3","Choice 3","images/file.png")
  Set pData(2) = $LB("1","Menu C","images/folderclosed.gif")
    Set pData(2,1) = $LB("2,1","Item 1","images/file.png")
    Set pData(2,2) = $LB("2,2","Item 2","images/file.png")
    Set pData(2,3) = $LB("2,3","Item 3","images/file.png")

  Quit $$$OK
}
The OnGetArray method uses the pMetaData argument to return a list of property names. You can use whatever property names you like, but also include in this list any or all of the following property names, which are given special handling by the <accordionMenu> component:
The OnGetArray Callback Method
<accordionMenu> passes the values provided for key, action, and targetId to the onselect event handler. The following simple example illustrates the availability of these values in the event handler.
ClientMethod itemSelected(key, action, targetId) [ Language = javascript ]
{
  alert(key + " " + action + " " + targetId);
}
The text provided in the caption property appears as a label for the menu item. The image file supplied by the image property appears as part of the menu item label, to the left of the text. You do not generally need to specify the children property, as it is filled in by the <altJSONProvider> based on subscripts in the pData array provided by the OnGetArray callback.
Navigator
The <navigator> component creates a combination navigation and simple settings interface similar to that found on mobile devices. This is an HTML5 component. It works correctly only on HTML5 compliant browsers. The <navigator> component is implemented by the %ZEN.Component.navigator class.
The navigator component is completely driven by JavaScript data. The only Zen object needed to drive the navigator is the navigator component itself. The component can operate in the following modes:
<navigator> supports the following properties:
Attribute Description
Zen group attributes A menu has the same style and layout attributes as any Zen group. For descriptions, see Group Layout and Style Attributes in the “Zen Layout” chapter of Using Zen.
backgroundStyle
Style to apply to the navigator background.
columnWidth
Width of columns (in pixels). The default value is 320, which is also the value that gives the best visual results.
disclosureWidth
Width in pixels of the disclosure bar on the left. The attribute showDisclosure controls visibility of the disclosure bar. Clicking on the disclosure bar expands and contracts the navigator.
expanded
If true, then show the navigator. If false, the navigator is contracted, and not visible. The default value is true.
footerHeight
Height of the footer (in pixels). Set to 0 for no footer. The default value is 0. A value of 40 shows the footer with the best visual results.
headerHeight
Height of the header (in pixels). The default value is 40, which is also the value that gives the best visual results.
onarrange
The onarrange event handler. See Zen Component Event Handlers.” Zen invokes this handler when the order of items in the current sheet has changed. This event is passed 3 arguments: key, swap, and final. final is true when a value is finished changing. swap is an object with the property index and newIndex, containing the index of the item to move and its new location.
onbuttonclick
The onbuttonclick event handler. Zen invokes this handler when the user has clicks on a "header" or "footer" button.
onchange
The onchange event handler. Zen invokes this handler when a control within the property sheet has changed value. This event is passed 3 arguments: key, value, and final. final is true when a value is finished changing (such as when the user stops pressing a stepper button).
onclosebuttonclick
The onclosebuttonclick event handler. Zen invokes this handler when the user clicks on a "close" button for an item.
onexpand
The onexpand event handler. Zen invokes this handler when the user expands or contracts this component.
ongetcontent
The ongetcontent event handler: This defines the client-side code that defines the content of a "sheet" within this component. This is passed level, key, and value as arguments. This code should return an object with any of the following properties:
  • title – the title to display for the sheet.
  • url – if defined, the url to display as an iframe in the sheet (in the same domain).
  • html – custom html to display within the sheet.
  • childIndex – index number (0-based) of child of this component to display.
  • items – array of JavaScript objects used to define the contents.
onindent
The onindent event handler. Zen invokes this handler when the indentation level of an item in the current sheet has changed. This event is passed 3 arguments: key, list, and final. final is true when a value is finished changing. list is a an array containing the new ordinal positions of the items.
onpopupaction
The onpopupaction event handler. Zen invokes this handler when the user has invoked and applied a popup for an item.
onselect
The onselect event handler. Zen invokes this handler when a new choice has been selected within the property sheet. This handler is also called when a "drill" item is selected. This event is passed 3 arguments: key, value, and which. The value of which is "select" or "drill".
showDisclosure
If true, show the disclosure bar on the left. The attribute disclosureWidth controls width of the disclosure bar.
Creating and Sizing a <navigator>
Adding a navigator component to a page displays an empty navigator panel:
<navigator id="navigator"/>
You can use CSS to control the size of the navigator, either in a style sheet or in JavaScript. For example, if the navigator has the id "navigator", the following code sets the height of the navigator to 600 pixels:
#navigator {
    height: 600px;
}
You can use the headerHeight attribute to control the height of the header banner:
<navigator headerHeight="10"/>
You can also programmatically set the header height using the navigator's setHeight method, which also readjusts any internal geometry.
You can use the columnWidth attribute to control the width of the navigator. However, be aware that the navigator is designed for a fixed width of 320 pixels. The layout of menu controls does not display well at other widths.
You can use the expanded attribute to hide the navigator by completely collapsing it into a thin strip. The navigator defines an onexpand event that fires whenever the navigator is expanded or contracted. You can use this event to adjust the layout of other controls on the page when the navigator changes size.
Adding Content to the Navigator
The key to adding content is the ongetcontent event handler, which is called whenever the navigator needs to get content to display.
It is important to understand the stack-based nature of the navigator. You can think of the navigator as being a stack of playing cards. The navigator initially displays one card, and the ongetcontent event handler is called to get content for that card. If the user drills down into an item in the navigator, then a new card is placed on top of the previous card and ongetcontent is called again to get the contents of the new card. The user may then go back to the previous card or drill down yet another level.
To define content for the initial card (referred to as a sheet within the navigator API), define an ongetcontent handler:
<navigator ongetcontent="return zenPage.getContentForLevel(level,key);"/> 
Note that you need to use return so that the return value of the method is passed along. The callback method is passed two parameters:
Here is a basic implementation of an ongetcontent handler method:
ClientMethod getContentForLevel(level, key) [ Language = javascript ]
{
    var content = { title: 'My Navigator', items:[] };
    return content;
}
The ongetcontent handler is expected to return an object with certain properties in it:
The following example returns some content:
ClientMethod getContentForLevel(level, key) [ Language = javascript ]
{
    var content = { title: 'My Navigator', items:[] };

    switch (key) {
    case '':
     // root
      content.items[content.items.length] = 
       {display:'caption', caption:'Red'};
      content.items[content.items.length] = 
       {display:'caption', caption:'Green'};
      content.items[content.items.length] = 
       {display:'caption', caption:'Blue'};
     break;
    }

    return content;
}
key is '' for the initial sheet. This example returns 3 items in the items array. Each item is an object with certain properties in it, in this case display, which specifies what to display and caption, the caption to display for each item.
At this point, the navigator does not do anything very interesting. You can use an onselect event handler to implement additional behavior. This event handler is called when the user selects an item:
<navigator ... onselect="alert(key);" ... />
This also has no effect, because there are no actions defined for the items in the navigator. Change the definition of the items so that they define action and key:
{display:'caption', caption:'Red', action:'select', key:'red'};
{display:'caption', caption:'Green', action:'select', key:'green'};
{display:'caption', caption:'Blue', action:'select', key:'blue'}; 
When the user clicks on an item in the navigator, the onselect action fires and passes along the key value.
The available values for action are:
The following additional item uses the 'drill' action:
content.items[content.items.length] = 
 {display:'caption', caption:'Gray', action:'drill', key:'gray'};
The navigator now displays Gray as an option, with a drill-down icon. If we click on Gray, a new sheet appears on the navigator.
To supply content for this sheet, we need to modify our ongetcontent handler to add a case for when the value of key is 'gray':
switch(key) {
 case '':
   // root
   content.items[content.items.length] =
    {display:'caption', caption:'Red', action:'select', key:'red'};
   content.items[content.items.length] =
    {display:'caption', caption:'Green', action:'select', key:'green'};
   content.items[content.items.length] =
    {display:'caption', caption:'Blue', action:'select', key:'blue'};
   content.items[content.items.length] =
    {display:'caption', caption:'Gray', action:'drill', key:'gray'};
   break;
 case 'gray':
   // gray
   content.items[content.items.length] =
    {display:'caption', caption:'Black', action:'select', key:'black'};
   content.items[content.items.length] =
    {display:'caption', caption:'White', action:'select', key:'white'};
   break;
}
A more sophisticated onselect callback makes the page behavior more interesting:
<navigator ... onselect="zenPage.selectHandler(key);" ... />
ClientMethod selectHandler(key) [ Language = javascript ]
{
 zenPage.getEnclosingDiv().style.background = key;
 while (zen('navigator').popSheet());
}
Now when the user selects an item, the color of the page changes, assuming that the value of key is a valid color value. The call to popSheet(), restores the navigator to its first sheet (keep popping until the stack level is back to 0).
You can also define a value for each item that is also passed to the onselect callback. In this case it is better to use value for the color name. In the following item definition, value specifies a darker shade of red:
{display:'caption', caption:'Red', action:'select', key:'red', value:'#330000'}; 
Pass value to the onselect callback, and use it to set the page color:
<navigator ... onselect="zenPage.selectHandler(key, value);" ... />
ClientMethod selectHandler(key, value) [ Language = javascript ]
{
  zenPage.getEnclosingDiv().style.background = value;
  while (zen('navigator').popSheet());
}
Changing the Display and Appearance of Items
Each item can define a display attribute. This can take one of the following values:
The image is specified by the image property of the item:
{display:'caption-image', caption:'Red', 
  image:'deepsee/blueprint_plan_48.gif', action:'select', 
  key:'green', value:'green'};
If image is not defined, a default image is used. If you do not want an image, but want the same indent as if there were an image, then use image:'none'.
You can also add properties that control the style of an item:
Editing Values in Items
A navigator can do more than act as a menu. You can use it to display and edit values using a small set of edit controls. These controls include:
Perform the following steps to display an edit control:
Here is an example of content items defining the various controls:
case 'edit':
 // edit controls
 content.items[content.items.length] = 
  {display:'caption-value-hz', caption:'String', 
   edit:'string', key:'string', value:''};
 content.items[content.items.length] = 
  {display:'caption-value-hz', caption:'Slider', 
   edit:'slider', key:'slider', value:0, minValue:0, maxValue:100};
 content.items[content.items.length] = 
  {display:'caption-value-hz', caption:'Stepper', 
   edit:'stepper', key:'stepper', value:0, minValue:0, maxValue:100};
 content.items[content.items.length] = 
  {display:'caption-value-hz', caption:'Stepper 2', 
   edit:'stepper-value', key:'stepper-value', value:0, 
   minValue:0, maxValue:100};
 content.items[content.items.length] = 
  {display:'caption-value-hz', caption:'Switch', 
   edit:'switch', key:'switch', value:0};
 content.items[content.items.length] = 
  {display:'caption-value-hz', caption:'Choice', 
   edit:'choice', key:'choice', value:'box', 
   valueList:'box,circle', displayList:'Box,Circle'};
 break;
Note that the action property is ignored if you define an edit property. value is the initial value of the control. It is not updated. You need to define an onchange callback to apply any changes and then make sure these values are used when the ongetcontent handler is next called.
The onchange callback is connected to the navigator component:
<navigator onchange="zenPage.dataChange(key,value,final);" ... 
The onchange callback is passed 3 arguments: the key value for the item that has changed, the new value, and a final flag. The final flag is true if the value is done changing, and is relevant to sliders and steppers. It lets you track changes as they occur, or wait until they are complete.
Creating a Multiple Choice Item
If you want to let the user select a value from a set of choices and the choice control is too restrictive, you can create a sheet containing multiple choices and then let the user drill down to select one. The following example defines a simple list to select a political party. First, define a property on the page to hold the current choice:
Property party As %String;
The following item displays this property:
content.items[content.items.length] = 
 {display:'caption-value-hz', caption:'Party', action:'drill', 
  key:'party', value:this.party};  
This item displays both the caption and the value, which is this.party. The action is drill. When the user drills down on this item, the ongetcontent handler asks for the contents for the sheet identified by the key 'party'. This sheet provides the list of political parties:
case 'party':
 content.items[content.items.length] = 
  {display:'value', selected:this.party=='Democrat', 
   value:'Democrat', action:'apply'};
 content.items[content.items.length] = 
  {display:'value', selected:this.party=='Independent', 
   value:'Independent', action:'apply'};
 content.items[content.items.length] = 
  {display:'value', selected:this.party=='Libertarian', 
   value:'Libertarian', action:'apply'};
 content.items[content.items.length] = 
  {display:'value', selected:this.party=='Republican', 
   value:'Republican', action:'apply'};
 break;
For each of these items, the action is 'apply’. When the user selects an option the onchange handler for the navigator is called and the sheet is popped off the stack. Note the use of selected to indicate the current value. The onchange handler updates the property party, so that when the original sheet is displayed, it shows the user's choice.
ClientMethod dataChange(key, value, final) [ Language = javascript ]
{
 // apply change to data model
 switch (key) {
 case 'party':
  this.party = value;
  break;
 }
}
Displaying HTML
The content object returned by the ongetcontent handler can return arbitrary HTML to display in a navigator sheet. To do this, define a property called html in the content object, and set it to include valid HTML:
switch(key) 
 case 'custom':
  content.html =
   '<font size="3" color="red">This is some text!</font>'
  content.title = 'HTML'
  break;
The html content fires data change events by invoking the navigator's applyValue method. This fires the onchange handler using the key value for the entire panel, which in this case is 'custom'.
Toolbar
The <toolbar> component is a versatile component that can display a series of different items along a horizontal bar. This is an HTML5 component. It works correctly only on HTML5 compliant browsers. The <toolbar> component is implemented by the %ZEN.Component.toolbar class.
<toolbar> has the following attributes:
Attribute Description
imageStyle Additional style to apply to images in the menu. Use this to change the size of images.
onchange onchange event handler. Provides notification that a control in the toolbar has changed value. This event is passed the following arguments: key, value, and final. final is true when a value is finished changing.
ongetdata ongetdata event handler. If defined, this event returns an array of items to be displayed in the menu.
onpagechange onpagechange event handler. If defined, this event is fired when the user selects a new page number from a "pages" item. This event is passed the following arguments:  key and page (selected page, 1-based). from the data element associated with the menu choice.
onselect onselect event handler. If defined, this event is fired when the user clicks on a item in the menu. This event is passed the following arguments: key, action, and targetId from the data element associated with the menu choice.
scrollOffset The index (0-based) of the first top-level item to be display when scrolled.
selectedIndex The index (0-based) of the selected item in the top-level menu.
style Additional style to apply to items in the menu.
You can display the following types of item in the toolbar:
You define the contents of the toolbar with a JavaScript object, which you can supply with the ongetdata event handler or by connecting to an <altJSONProvider>. The JavaScript object is assumed to have a collection named children that define a set of objects that specify the items in the toolbar. Each of these objects has a type property that indicates what type of item to display. The caption is the caption. and key is a key used to identify an item, such as in a callback event.
For example, the following Zen page and ongetdata event handler create an example toolbar:
XData Contents [ XMLNamespace = "http://www.intersystems.com/zen" ]
{
  <page xmlns="http://www.intersystems.com/zen" title="">
    <toolbar label="Test Toolbar" width="900px" 
     ongetdata="return zenPage.myData();" />
  </page>
}
ClientMethod myData() [ Language = javascript ]
{
  var data = {
  children:[
  { caption:"Home", key:"menuHome", type:'item' },
  { caption:"Menu", key:"menuMenu", type:'item',
  children:[
  { caption:"Menu 1", key:"menu1", type:'item'},
  { caption:"Menu 2", key:"menu2", type:'item'},
  { caption:"Menu 3", key:"menu3", type:'item'}
  ]
  },
  { caption:"", key:"menuChoice", type:'choice', valueList:'On,Maybe,Off' },

  { caption:"Tab 1", key:"menuTab1", type:'tab' },
  { caption:"Tab 2", key:"menuTab2", type:'tab', selected:true },
  { caption:"Tab 3", key:"menuTab3", type:'tab' },
  { caption:"This is a message", key:"menuMsg", type:'message' },
  { html:'<input type="text"/>', key:"menuSearch", type:'item' },
  { caption:'Page', key:"menuPages", type:'pages', minValue:1,maxValue:20,value:15 },
    ]
  };
  return data;
}
Tabs
The following Zen components support tabbed menus and forms for Zen applications:
<tabGroup>
A <tabGroup> displays a set of <tab> components. It can only display one <tab> at a time. The user can choose one of these tabs to be displayed on top of the set. A <tabGroup> looks like the following example, based on the class ZENTest.TabTest in the SAMPLES namespace.
In the above example, the user has clicked the tab labelled “First Page.” In the following example, the user has clicked the tab labelled “Second Page” and has clicked the down-arrow to select a “Home City” option from the <dataCombo> control.
The <tabGroup> definition that produces these illustrations is shown below:
<tabGroup id="tabGroup" showTabBar="true"
          onshowTab="zenPage.updateButtons();" remember="true">
  <tab caption="First Page">
    <hgroup>
      <spacer width="15" />
      <vgroup>
        <spacer height="5"/>
        <html>This is the first tab!</html>
        <form width="75%" layout="vertical"
              cellStyle="padding: 2px; padding-left: 5px; padding-right: 5px;"
              groupStyle="border:1px solid darkblue;">
          <titleBox title="My Form" titleStyle="background: #DDDDFF;"
                    containerStyle="padding: 0px;" />
          <spacer height="5"/>
          <colorPicker title="This is a custom control!"
                       label="Color:" name="Color" />
          <text label="Color Name:" name="ColorName" size="12" />
          <text label="DOB:" id="DOB" name="DOB" size="15"
                maxlength="10" valign="bottom"/>
          <dataCombo label="Patient:" name="Patient" size="24"
                     sql="SELECT Name FROM ZENDemo_Data.Employee
                          WHERE Name %STARTSWITH ? ORDER BY Name"/>
        </form>
      </vgroup>
    </hgroup>
  </tab>
  <tab caption="Second Page">
    <spacer height="5"/>
    <html>This is the second tab!</html>
    <dataCombo label="Home City:" name="City" size="24"
               sql="SELECT Location FROM ZENApp_Data.Customer
                    WHERE Location %STARTSWITH ? ORDER BY Name"/>
  </tab>
  <tab caption="Third Page" tabResource="MyResource">
    <spacer height="5"/>
    <html>This is the third tab!</html>
    <dynaGrid id="grid">
      <gridColumn label="Name"   width="25%" />
      <gridColumn label="Salary" width="25%"  />
      <gridColumn label="Comment" width="50%" />
      <gridRow label="R1" />
      <gridRow label="R2" />
      <gridRow label="R3" />
    </dynaGrid>
  </tab>
</tabGroup>
When a <tabGroup> has contents that do not fit within its defined height or width, Zen clips the display of excess content at the right and bottom edges of the defined <tabGroup> size, leaving room for a horizontal or vertical scrollbar that allows the user to scroll to view the contents. Application and page level stylesheets can override this default behavior by applying the CSS property overflow to the element class tabGroupBody.
<tabGroup> has the following attributes. Since either <tabGroup> or <lookoutMenu> may display <tab> components, both <tabGroup> and <lookoutMenu> share these attributes. <lookoutMenu> has additional attributes. For descriptions, see the <lookoutMenu> section.
Tab Group Attributes
Attribute Description
Zen group attributes <tabGroup> and <lookoutMenu> each have the same style and layout attributes as any Zen group. For descriptions, see Group Layout and Style Attributes in the “Zen Layout” chapter of Using Zen. For <tabGroup>, the height attribute has no effect; it is necessary to control the height of the <tabGroup> from the CSS stylesheet.
currTab
1–based sequential number of the tab currently displayed. The default is 1.
The currTab value may contain Zen #()# runtime expressions.
onhideTab
The onhideTab event handler for the menu item. Zen invokes this handler when a previously displayed tab becomes hidden. See Zen Component Event Handlers.”
onshowTab Client-side JavaScript expression that Zen invokes when a previously hidden tab becomes the displayed tab. Generally this expression invokes a client-side JavaScript method.
remember
If true, remember the most recently displayed tab number in a session cookie and return to this tab when redisplayed. The default is false.
remember has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
showBody
If true, display the set of tabs that belong to this <tabGroup>. By setting showBody to false, and setting showTabBar true, you can display a set of tab bar buttons with no tab contents underneath. The default for showBody is true, and for showTabBar is false.
showBody has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
showTabBar
If true, display a set of tab buttons along the top of this group. The default is false.
showTabBar has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
<lookoutMenu>
A <lookoutMenu> presents a stack of buttons, one for each tab. Clicking on a button shifts the other buttons down so that they display below the contents of the currently selected tab.
The following example is based on the class ZENTest.LookoutMenuTest in the SAMPLES namespace. In the illustration, the user has clicked the “Animal” button to reveal the contents of that tab. This selection moved the remaining buttons to the bottom of the lookout menu. The cursor is now on the “Vegetables” menu item. If the user clicks this item, the content of its link is activated as defined in the corresponding <menuItem>.
A <lookoutMenu> contains a set of <tab> components, which in turn contain <menuItem> and <menuSeparator> components. These tabs are groups that can contain any component, but when they are present inside a <lookoutMenu> they contain the components that would normally define a <menu>.
The <lookoutMenu> definition that produced the above illustration follows:
<lookoutMenu id="lookout" expandable="true">
  <tab caption="Animal" id="animal">
    <menuItem caption="Mineral"
              link="javascript: zenPage.toggleTab('mineral');"
              image="images/folder.gif" />
    <menuItem caption="Vegetables"
              link="javascript: zenPage.toggleTab('vegetable');"
              image="images/folder.gif" />
    <menuItem caption="Cheese"
              link="javascript: zenPage.toggleTab('cheese');"
              image="images/folder.gif" />
  </tab>
  <tab caption="Mineral" id="mineral" tabResource="MyResource">
    <form>
      <text label="Name:" />
      <text label="Weight:" />
    </form>
  </tab>
  <tab caption="Vegetable" id="vegetable" disabled="false">
    <menuItem caption="Menu A"
              link="javascript: alert('A');"
              help="Option A" image="images/folder.gif" />
    <menuItem caption="Menu B"
              link="javascript: alert('B');"
              help="Option B" image="images/folder.gif" />
    <menuSeparator />
    <menuItem caption="Disable"
              link="javascript: zenPage.toggleTab('vegetable');"
              image="images/folder.gif" />
  </tab>
  <tab caption="Cheese" id="cheese">
    <menuItem caption="Menu C" link="javascript: alert('C');"
              help="Option C" image="images/folder.gif" />
  </tab>
</lookoutMenu>
<lookoutMenu> has the following attributes:
Attribute Description
Zen tab group attributes <lookoutMenu> has the same general-purpose attributes as <tabGroup>. For descriptions, see the Tab Group Attributes table in the <tabGroup> section.
expandable
If true, this <lookoutMenu> group is expandable. The default is false.
When expandable is true, an expansion bar displays along the top of the <lookoutMenu> as shown in the illustration above. The user clicks on this bar to toggle the expanded state of the <lookoutMenu>. When the menu is contracted, only the expansion bar is visible. The user clicks the bar again to expand the <lookoutMenu>.
This attribute has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
expanded
If true, this <lookoutMenu> group is expanded (children visible). If false, it is contracted (children not visible). The default is true.
expanded has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
oncontract
The oncontract event handler for the <lookoutMenu>. Zen invokes this handler just before contracting (hiding) the children of this <lookoutMenu> group. See Zen Component Event Handlers.”
onexpand Client-side JavaScript expression that Zen invokes just before expanding (displaying) the children of this <lookoutMenu> group.
<tab>
A <tab> is a specialized group that defines a tab within a <tabGroup> or <lookoutMenu>. <tab> has the following attributes:
Attribute Description
Zen group attributes
<tab> has the same style and layout attributes as any Zen group. For descriptions, see Group Layout and Style Attributes in the “Zen Layout” chapter of Using Zen.
A <tabGroup> or <lookoutMenu> uses the hidden attribute to make the current tab visible and all other tabs invisible. That is, when a <tabGroup> or <lookoutMenu> is displayed, it sets the hidden attribute for the currently visible <tab> to false and sets all the others to true.
For this reason, explicitly setting hidden for a <tab> element has no effect. If you need to prevent users from accessing a <tab> you can set its disabled attribute to true.
caption
Text for this tab when it appears as a choice within its containing <tabGroup> or <lookoutMenu>.
Although you can enter ordinary text for this attribute, it has the underlying data type %ZEN.Datatype.caption. See Zen Attribute Data Types.”
link
An optional URI value. If defined, the tab displayed within the <tabGroup> bar becomes a link referring to this URI. No link is displayed for the current tab.
The link value may contain Zen #()# runtime expressions.
tabResource Name of a Caché resource. The user must have privileges to access this resource or this tab becomes disabled. If you are not familiar with Caché resources, see the Assets and Resources chapter in the Caché Security Administration Guide.
Trees
The following Zen components each provide a hierarchical outline of links. The outline expands or contracts in response to user clicks:
<expando>
The <expando> is a group with the ability to show or hide its children. A right-arrow graphic () beside the <expando> label indicates that the group is expanded (displayed), and a down-arrow graphic () indicates that the group is contracted (hidden). A fully contracted <expando> group looks like the following example.
The user expands the group by clicking on its label. The contents of the <expando> then display below the label. As a Zen group, an <expando> may contain any type of Zen component. In particular, each level of the <expando> may contain additional, nested <expando> groups that can be expanded or contracted independently of the containing <expando> group.
In the following example, the user has just clicked on the label to expand the group. The cursor is now poised to contract the <expando> by clicking the label again. This example contains three nested <expando> groups. The innermost <expando> uses a horizontal layout, and contains <button> components instead of <titleBox> components as in the other levels.
The <expando> definition that produced this example follows:
<expando caption="Landscape Architect's Bookshelf"
         childIndent="35px" remember="true">
   <spacer height="1" />
   <titleBox title="National Arboretum"
             subtitle="Book of Outstanding Garden Plants" />
   <titleBox title="Perennials"
             subtitle="The Definitive Reference" />
   <titleBox title="Japanese Gardens"
             subtitle="Design Principles, Aesthetic Values" />
   <spacer height="1" />

   <expando caption="Graham Stuart Thomas" OnDrawContent="DrawContent"
            childIndent="35px" remember="true">

      <expando caption="Three Gardens of Pleasant Flowers"
               layout="horizontal" childIndent="35px" remember="true">
         <button caption="Cambridge" onclick="zenPage.cambridgeClick()"/>
         <button caption="Oak Cottage" onclick="zenPage.oakCottageClick()"/>
         <button caption="Briar Cottage" onclick="zenPage.briarCottageClick()"/>
      </expando>

      <titleBox title="Ornamental Shrubs"
                subtitle="Climbers and Bamboos" />
      <titleBox title="Perennial Garden Plants"
                subtitle="or The Modern Florilegium" />
      <titleBox title="The Art of Planting"
                subtitle="or The Planter's Handbook" />
   </expando>

   <spacer height="1" />
   <titleBox title="Trees and Shrubs"
             subtitle="for Dry California Landscapes" />
   <titleBox title="Architectural Graphic Standards"
             subtitle="of the American Institute of Architects" />
</expando>
This definition references a number of methods defined in the page class. For example, the second-level <expando> references the server-side callback method DrawContent to produce additional formatted text, in this case a list of honors awarded to the author of the books listed. DrawContent looks like this:
Method DrawContent(ByRef expando As %ZEN.Component.expando) As %Status
{
  &html<<b> (OBE, VMH, DHM, VMM)</b>>
  Quit $$$OK
}
The <expando> component has the following attributes:
Attribute Description
Zen group attributes <expando> has the same style and layout attributes as any Zen group. For descriptions, see Group Layout and Style Attributes in the “Zen Layout” chapter of Using Zen. The default layout for <expando> is vertical.
animate
If true, Zen animates the appearance and disappearance of the <expando> group contents. If false, it does not.
animate has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
caption
Text to display as the title of this <expando> group. This text displays even when the <expando> is contracted. This text is not automatically HTML escaped.
Although you can enter ordinary text for this attribute, it has the underlying data type %ZEN.Datatype.caption. See Zen Attribute Data Types.”
childIndent HTML length value giving the amount to indent the children in the expanded list of items. The above example uses 35px. The default is to use no indentation, aligning the children with the caption.
expanded
If true, this <expando> group is expanded (children visible). If false, it is contracted (children not visible). The default is true.
expanded has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
framed
If true, display a border around the entire group and display the caption within a more formal title box. This more formal version of the <expando> component is also known as a disclosure. The default is false.
framed has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
The %ZEN.Component.expando class offers a server-side callback method %OnDrawTitleOptions which, if defined in a subclass of %ZEN.Component.expando, provides a way to add content to the right side of the title bar when framed is true. Any HTML written by %OnDrawTitleOptions is injected into the title bar when the <expando> is displayed.
imageContracted
URI of the image to display when the <expando> group is contracted. The path that you provide for imageContracted must be relative to the CSP directory in the Caché installation directory. The default for imageContracted is a right-arrow graphic () located at the URI broker/images/disclosure-contracted.gif.
imageExpanded
URI of the image to display when the <expando> group is expanded. The path that you provide for imageExpanded must be relative to the CSP directory in the Caché installation directory. The default for imageExpanded is a down-arrow graphic () located at the URI broker/images/disclosure-expanded.gif.
oncontract
The oncontract event handler for the <expando>. Zen invokes this handler just before contracting (hiding) the children of this <expando> group. See Zen Component Event Handlers.”
OnDrawContent
Name of a server-side callback method in the Zen page class. This method injects HTML content into the <expando> using &html<> syntax or WRITE commands.
Zen invokes this method whenever it draws the <expando>, automatically passing it a %String that contains the seed value from the <expando>. The callback must return a %Status data type. The following is a valid method signature:
Method DrawMe(pSeed As %String) As %Status
To use the above method as the callback, the developer would set OnDrawContent="DrawMe" for the <expando>.
onexpand Client-side JavaScript expression that Zen invokes just before expanding (displaying) the children of this <expando> group.
remember
If true, remember the most recent expanded state in a session cookie and return to this state when redisplayed. The default is false.
remember has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
<expando> can be particularly useful in navigation when combined with <link>.
<dynaTree>
The <dynaTree> component displays a hierarchical collection of user-defined items as an expandable tree. In many ways <dynaTree> is similar to <expando>. However, instead of specifying the components that it contains, <dynaTree> acquires its contents dynamically at runtime. You can provide data for a <dynaTree> as follows:
Note that <dynaTree> builds the tree from the root of the global or array. It does not support starting the tree from a node other than the base node.
<dynaTree> Using a Global
The simplest way to define a <dynaTree> is to use a Caché global (multidimensional array). For background information about globals, refer to the book Using Caché Globals.
The following <dynaTree> definition references the ^myTree global by providing the dataGlobal attribute:
<dynaTree id="tree" dataGlobal="^myTree" /> 
This definition causes the contents of the ^myTree global to be displayed on the page as nodes within an expandable tree.
Node Labels
Suppose you define ^myTree using the following statements at the Terminal command line:
 Set ^myTree("Root","Item 1") = "ZENMVC.MVCForm.cls"
 Set ^myTree("Root","Item 2") = "http://www.intersystems.com"
 Set ^myTree("Root","Item 2","SubItem") = "ZENDemo.Home.cls"
Each global node now has one or more subscripts, such as "Root" or "Item 2". Zen uses these subscripts as the display values for nodes in the <dynaTree>. The logical value for each node is the value of the global at that subscript. Our ^myTree global can now generate a <dynaTree> that looks like the following illustration. This <dynaTree> is fully expanded, with the user holding the cursor over the node "SubItem" at the lowest level. Based on the previous SET statements, the logical value for this node is "ZENDemo.Home.cls" and its display value is "SubItem"
When you use a global to define a <dynaTree>, the resulting nodes always appear in alphabetical order (by label) because that is the way globals are organized in the database.
Node Values
When the user clicks on a <dynaTree> node label such as:
"SubItem"
The corresponding node value is triggered. If the values are set as described in the Node Labels section of this chapter, then this value is:
"ZENDemo.Home.cls"
Each node value is a string. Typically the node values are URI values. For example:
If you want a link to invoke JavaScript, you can start the URI with the string javascript: as in the following examples:
Parameters for <dynaTree> Callback Methods
A <dynaTree> can contain zero or more <parameter> elements. Each <parameter> specifies an input parameter for the callback method that generates the <dynaTree>. This callback method might be:
The <parameter> element has the following attributes:
Attribute Description
paramName The paramName must be unique within the <dynaTree>. It becomes a subscript in the array of parameters passed to the callback method.
value The value supplied for a <parameter> can be a literal string, or it can contain a Zen #()# runtime expression.
<dynaTree> OnGetNodeInfo Callback Method
<dynaTree> can get its list of nodes by invoking a server-side callback method defined in the page class. The method name is specified using the OnGetNodeInfo attribute. For example:
<dynaTree id="myTree"
          OnGetNodeInfo="GetNodeInfo"
          onclick="zenPage.treeClick(zenThis);" >
  <parameter paramName="a" value="10" />
  <parameter paramName="b" value="20" />
</dynaTree>
This example defines a tree whose nodes are provided by the server-side callback method GetNodeInfo. When the user clicks on an item, the treeClick method is called. The example also provides two parameters for the GetNodeInfo method.
The OnGetNodeInfo callback is called repeatedly to get information about each node displayed within the tree. Zen handles this repetition as follows:
The OnGetNodeInfo callback method must have a signature that looks like this:
Method GetNodeInfo(Output tSC As %Status,
    ByRef pParams As %String,
    pLevel As %Integer,
    ByRef pHandle As %String,
    pNodeInfo As %ZEN.Auxiliary.NodeInfo) As %Boolean
Where:
Property Description
expanded
If true, this node (if it has children) is initially displayed as expanded. If false, it is initially displayed as contracted. The default is true.
expanded has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.” It has the value 1 or 0 in server-side code, true or false in client-side code.
hasChildren
If true, this node has one or more child nodes. If this is the case, the next call to the OnGetNodeInfo callback fetches information about these child nodes. The default is true.
hasChildren has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.” It has the value 1 or 0 in server-side code, true or false in client-side code.
link
If non-empty, link defines a link that is used if the user clicks on this node. This can be a URI such as the name of a page to display, or a JavaScript expression. If you want to invoke a client-side JavaScript method in the link, start the URI with javascript: as in:
When providing a JavaScript expression, use double quotes to enclose the value and single quotes (if needed) inside the expression.
text A string containing a value to display for this node within the tree.
value A string containing a logical value to associate with this node within the tree.
The following code example provides a complete OnGetNodeInfo method.
XData Contents [XMLNamespace="http://www.intersystems.com/zen"]
{
<page xmlns="http://www.intersystems.com/zen" title="">
<dynaTree id="myTree" OnGetNodeInfo="GetNodeInfo">
</dynaTree>
</page>
}

Method GetNodeInfo(
    Output tSC As %Status, ByRef pParams As %String,
    pLevel As %Integer, ByRef pHandle As %String,
    pNodeInfo As %ZEN.Auxiliary.NodeInfo) As %Boolean
{
    // We store our personal state information in pHandle that is passed,
    // unchanged, to each call to the callback.
    // In the beginning: pHandle = ""
    If pHandle = "" 
    {
        Set pHandle = 1
    }
    // Let's create 10 nodes
    ElseIf (pHandle = 10)
    {
        Quit 0
    }
    // Set the node count as name and value
    Set pNodeInfo.value = "Node "_pHandle
    Set pNodeInfo.text = "test"_pHandle
    Set pNodeInfo.expanded = 1
    
    // We add subnode within the 3rd node...
    If (pHandle = 3)
    {
        Set pNodeInfo.hasChildren = 1
    }
    // ...and also subnode for the 6th subnode of the 3rd node.
    ElseIf (pHandle = 6)
    {
        Set pNodeInfo.hasChildren = 1
    }
    
    Set pHandle = pHandle + 1
    
    Quit 1
  }
<dynaTree> OnGetTreeInfo Callback Method
<dynaTree> can get its complete tree as an multidimensional array of node information, by invoking a server-side callback method defined in the page class. The method name is specified using the OnGetTreeInfo attribute. For example:
<dynaTree id="tree"
          OnGetTreeInfo="GetTreeInfo"
          onclick="zenPage.treeClick(zenThis);">
  <parameter paramName="count" value="20"/>
</dynaTree>
The OnGetTreeInfo callback method must have a signature that looks like this:
ClassMethod GetTreeInfo(pRoot As %String, Output pTree, ByRef pParms) As %Status
Where:
The previous example defines a <dynaTree> whose data is provided by the server-side callback method GetTreeInfo. When the user clicks on an item, the treeClick method is called with the value 20 in its parameter array subscripted by the key "count".
Important:
If you use OnGetTreeInfo to define the <dynaTree> this excludes using the OnGetNodeInfo or dataGlobal attributes in the same <dynaTree>.
The following example implements GetTreeInfo from the previous example. This shows the required method signature for an OnGetTreeInfo callback. For further examples, see the class ZENTest.DynaTreeTest in the SAMPLES namespace. In that example there are several different OnGetTreeInfo callbacks. The user clicks a radio button to choose which of these callbacks is used to generate the <dynaTree> on this page.
ClassMethod GetTreeInfo(pRoot As %String, Output pTree, ByRef pParms) As %Status
{
  if pRoot=""
  {
    #; top-most nodes are children of 0
    Set pTree(0,"ch",1) = ""
    Set pTree(0,"ch",2) = ""
    Set pTree(0,"ch",3) = ""

    #; each node supplies: $LB(caption, value, hasChildren, link, expanded, icon)
    Set pTree(1) = $LB("Animal","Animal",1,"",1,,"General types of animal")
    Set pTree(2) = $LB("Mineral","Mineral",1,"",1,,"General types of mineral")
    Set pTree(3) = $LB("Vegetable","Vegetable",1,"",1,,"General types of vegetable")
  }
  elseif pRoot="Animal" //id 1
  {
    Set pTree(4) = $LB("Mammal","Mammal",1,"",1)
    Set pTree(0,"ch",4) = ""
  }
  elseif pRoot="Mineral" //id 2
  {
    Set pTree(7) = $LB("Rock","Rock",0,"",1)
    Set pTree(0,"ch",7) = ""
  }
  elseif pRoot="Vegetable" //id 3
  {
    Set pTree(8) = $LB("Fruit","Fruit",1,"",1)
    Set pTree(0,"ch",8) = ""
  }
  elseif pRoot="Mammal" //id 4
  {
    Set pTree(5) = $LB("Cat","Cat",0,"",1)
    Set pTree(6) = $LB("Dog","Dog",0,"",1)
    Set pTree(0,"ch",5) = ""
    Set pTree(0,"ch",6) = ""
  }
  elseif pRoot="Fruit" //id 8
  {
    Set pTree(9) = $LB("Apple","Apple",0,"",1)
    Set pTree(10) = $LB("Banana","Banana",0,"",1)
    Set pTree(11) = $LB("Cherry","Cherry",0,"",1)
    Set pTree(0,"ch",9) = ""
    Set pTree(0,"ch",10) = ""
    Set pTree(0,"ch",11) = ""
  }
  Quit $$$OK
}
<dynaTree> Attributes
The <dynaTree> component is the XML projection of the %ZEN.Component.dynaTree class. This topic has already described the dataGlobal, OnGetNodeInfo, and OnGetTreeInfo attributes in detail. The following table lists all the <dynaTree> attributes.
The purpose of many <dynaTree> attributes is to configure the images that the user clicks to control expansion and contraction. The following diagram shows many of the images that the <dynaTree> displays while the component is in use. The following table describes how to configure these images, if you wish to substitute your own images instead.
Attribute Description
Zen component attributes
<dynaTree> has the same general-purpose attributes as any Zen component. For descriptions, see these sections:
childIndent
HTML length value giving the amount by which each level within the dynaTree should be indented. The default is to use no indentation.
childIndent applies only when showLines is false. When showLines is true, indentation is controlled by the <dynaTree> to fit the lines that it provides.
dataGlobal
Name of a Caché global (multidimensional array) that can provide the contents of the <dynaTree>. This string must include the ^ prefix that is characteristic of Caché global names, for example:
Using dataGlobal excludes OnGetNodeInfo. For details, see the section <dynaTree> Using a Global in this chapter.
imageContracted
File name for the image to display when the <dynaTree> is contracted. The user clicks on this image to expand the group. The default for imageContracted is the plus sign whose file name is contracted.gif.
You may specify an alternate image. The image file that you identify must reside in the images directory under the Caché installation directory.
imageContracted applies only when showLines is false. When showLines is true, a plus sign automatically appears as part of the graphical image that displays the lines.
imageExpanded
File name for the image to display when the <dynaTree> is expanded. The user clicks on this image to contract the group. The default for imageExpanded is the minus sign whose file name is expanded.gif.
You may specify an alternate image. The image file that you identify must reside in the images directory under the Caché installation directory.
imageExpanded applies only when showLines is false. When showLines is true, a minus sign automatically appears as part of the graphical image that displays the lines.
imageFolderClosed
File name for the image to display beside a closed folder node. This is a node that has children but is currently closed (contracted). The user clicks on this image to expand the node. The default for imageFolderClosed is a closed folder icon whose file name is folderclosed.gif.
You may specify an alternate image. The image file that you identify must reside in the images directory under the Caché installation directory.
imageFolderOpen
File name for the image to display beside an open folder node. This is a node that has children and is currently open (expanded). The user clicks on this image to contract the node. The default for imageFolderOpen is an open folder icon whose file name is folderopen.gif.
You may specify an alternate image. The image file that you identify must reside in the images directory under the Caché installation directory.
imageNode
File name for the image to display beside a leaf node. This is a node with no children in the <dynaTree>. The user clicks on this image to select the node. The default for imageNode is a file icon whose file name is node.gif.
You may specify an alternate image. The image file that you identify must reside in the images directory under the Caché installation directory.
showLines
If true, show dashed lines between the nodes of the tree. The default is false.
This attribute has the underlying data type %ZEN.Datatype.boolean. See Zen Attribute Data Types.”
onchange
The onchange event handler for the <dynaTree>. Zen invokes this handler when the current value of the <dynaTree> changes. See Zen Component Event Handlers.”
onclick Client-side JavaScript expression that Zen invokes when the user clicks on a node within the <dynaTree>.
ondblclick Client-side JavaScript expression that Zen invokes when the user double-clicks on a node within the <dynaTree>.
OnGetNodeInfo Name of a server-side callback method that reports information about each node within the tree. Using OnGetNodeInfo excludes dataGlobal. For details, see the section <dynaTree> Using an OnGetNodeInfo Callback in this chapter.
OnGetTreeInfo Name of a server-side callback method that fills in a local array that supplies the contents of the tree. Using OnGetTreeInfo excludes both OnGetNodeInfo and dataGlobal. For details, see the section <dynaTree> Using an OnGetTreeInfo Callback in this chapter.
selectedIndex 0-based index of the currently selected node in the <dynaTree>. The default selectedIndex is –1 (nothing is selected).
When you work with %ZEN.Component.dynaTree programmatically, you must also know about the following properties of the dynaTree class:
<dynaTree> Drag and Drop
<dynaTree> supports data drag operations when its dragEnabled attribute is set to true. This is possible because each node in a <dynaTree> has a logical value and a display value defined.
Data drag is explained in the Data Drag and Drop section in the chapter “Zen Controls.” Essentially, the user clicks the mouse button down while the cursor is positioned on a <dynaTree> node, then moves the mouse away from the <dynaTree> while still holding down the button. Data drag captures the current value of the <dynaTree> node where the drag operation began. If the logical value and displayed value are different, this difference is preserved when the data is captured.
The data acquired from a <dynaTree> node can be dropped on any Zen component that has drop enabled. Generally this is a Zen control. <dynaTree> does not support data drop, as it is not a control and cannot accept data input from the client side.
Filters
Zen offers a <buttonView> component that allows you to lay out a set of navigation choices as buttons in a grid. Typically, <buttonView> is used to display progressive filter buttons that allow a user to narrow down a search, as on the InterSystems documentation home page:
In the previous example, the user has just clicked the JavaScript button. This has disabled all the other buttons in the same category as JavaScript, and has caused the Reset button to activate and change color. To re-enable all the buttons in this category, the user could click the JavaScript button again.
A <buttonView> may contain multiple categories of buttons. Each category works like a set of radio buttons, in that only one button in the category may be selected at any time. In the following example, many more categories in the <buttonView> have been selected.
Every <buttonView> component automatically provides a Reset button that is enabled only when the user has made at least one selection. Reset appears as the top left entry in the grid. If clicked, Reset cancels all current user selections in the <buttonView>.
<buttonView>
The <buttonView> component does not contain any other components. It provides its buttons dynamically, via a server-side callback method. <buttonView> has the following attributes:
Attribute Description
Zen component attributes
<buttonView> has the same general-purpose attributes as any Zen component. For descriptions, see these sections:
columns
Number of columns for the grid. If not specified, the default is 4. The number of rows is determined dynamically depending on the total number of items and the columns value.
OnGetButtonInfo Name of a server-side callback method that provides the list of items to display for this component. For details, see the <buttonView> OnGetButtonInfo section following this table.
onselect
The onselect event handler for the <buttonView>. Zen invokes this handler when the user selects a new button. The onselect handler method must have one input parameter that you can use to pass it the current string value of the <buttonView>. For details, see the <buttonView> onselect section following this table. See Zen Component Event Handlers.”
seed
Allows you to pass some arbitrary value to the OnGetButtonInfo callback. The seed value can be a literal string, or it can contain a Zen #()# runtime expression.
For suggestions about how to use the <buttonView> seed value, see the <buttonView> OnGetButtonInfo section following this table.
value
A string containing the currently selected value from the component.
Each entry in the <buttonView> value string takes the following form:
If there are multiple categories in the <buttonView>, and the user makes a selection in more than one category, Zen appends each subsequent choice to the existing value string. When the user has selected multiple buttons, the value string takes the following form:
The user can click the built-in Reset button to empty the value string and start over.
<buttonView> XData Contents
The following XData Contents excerpt defines a <buttonView> and an <html> component:
<hgroup width="100%" cellVAlign="top" cellAlign="left">
  <buttonView id="filter" columns="2"
              OnGetButtonInfo="GetFilterList"
              onselect="zenPage.filterChange(value);"
              />
  <spacer width="10"/>
  <html id="myContent" OnDrawContent="DrawMyContent"/>
</hgroup>
The next several topics explain how user selections in this <buttonView> can cause this <html> component to invoke its OnDrawContent method, effectively redrawing the <html> component based on the user’s selections in the <buttonView>.
<buttonView> OnGetButtonInfo
The server-side callback method identified by the <buttonView> OnGetButtonInfo attribute must be defined in the page class. It must return a %Status value, and it must have two input parameters in the following order:
To support the previous <buttonView> syntax example, the page class would also need to define a method called GetFilterList with the following signature. Zen automatically invokes the method and uses the array that it creates, whenever it displays the <buttonView> component:
ClassMethod GetFilterList(pSeed As %String, ByRef pInfo) As %Status
The OnGetButtonInfo callback method must fill the array that was passed to it by reference with a required category, caption, and value for each button. Optionally, each array entry can also contain tooltip text and a disabled flag for the button. An ObjectScript statement such as the following would correctly set the array entry for button number n:
Set pInfo(n) = $LB(category,caption,button,tooltip,disabled)
Where:
The following is a sample ObjectScript statement that defines a button for a <buttonView> by assigning a value to an entry in the pInfo array:
 Set pInfo($I(pInfo))=
 $LB("adv",$$$Text("Expert Tools"),"Expert",$$$Text("For expert users only"))
 
Where $I is shorthand for the ObjectScript function $INCREMENT.
<buttonView> onselect
The example in <buttonView> XData Contents identifies an onselect handler called filterChange that accepts one parameter, the current value of the <buttonView> component:
<buttonView id="filter" columns="2"
              OnGetButtonInfo="GetFilterList"
              onselect="zenPage.filterChange(value);"
              />
filterChange could then work as follows:
ClientMethod filterChange(list) [ Language = javascript ]
{
  var html = zen('myContent');
  var div = html.getEnclosingDiv();
  div.scrollTop = 0;
  html.seed = list;
  html.refreshContents();
}
This onselect handler does the following:
  1. Calls the JavaScript function zen to retrieve the <html> component that appears on the page along with the <buttonView>. This <html> component had the following definition in XData Contents:
    <html id="myContent" OnDrawContent="DrawMyContent"/>
    That is why filterChange uses this statement:
    var html = zen('myContent');
  2. Assigns the incoming <buttonView> value to the <html> seed attribute:
    html.seed = list;
  3. Refreshes the <html> component:
    html.refreshContents();
    This automatically invokes the OnDrawContent handler for the <html> component. This callback must be a server-side method defined in the page class. It must accept a %String as input and return a %Status data type. In this case, its name is DrawMyContent.
    DrawMyContent uses the <html> seed (that is, the <buttonView> value) to determine what to do while redrawing the <html> component. It is up to this method to figure out how to use the filtering information provided by multiple user selections in the <buttonView>. The <buttonView> simply provides a value.