Skip to main content

Navigation Components

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:

  • Links” link to other Zen pages, or other application content, via a URI.

  • Menus” present and manage a structured set of choices, usually links.

  • Navigator” creates a navigation interface similar to that found on mobile devices.

  • Tabs” define tabbed menus and tabbed forms.

  • Trees” provide a hierarchical outline of links that expands or contracts in response to user clicks.

  • Filters” allow you to filter the available choices by category.

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.tabOpens in a new 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.groupOpens in a new tab.

Zen Navigation Components
generated description: menu classes

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 submenu might display

  • A message might pop up

  • A different page might display

  • The contents of the current page might change

  • Something else might happen, depending on how you have programmed the menu to respond when the user selects each <menuItem>

A vertical menu, expanded to three levels, looks like the following example, based on the class ZENTest.MenuTestOpens in a new tab in the SAMPLES namespace. Any options that produce submenus use the » right angle quote to indicate this, as shown for “Submenu” and “Sub Submenu”.

generated description: menu vertical

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:

  • In many cases, the example uses JavaScript alerts in place of URI links for demonstration purposes. link values can take this form, or they can redirect the browser to other pages in the Zen application, as the example shows:

  • A <menu> can contain a <menu>. If so, the contained <menu> becomes a submenu and its caption is listed in sequence along with any <menuItem> components at the same level. The above example provides three levels of <menu>.

  • The example provides a <menuSeparator> on the first- and third-level menus. Note how the <menuSeparator> component generates the divider bar between “Submenu” and “Test Suite” and between “Sub Submenu” and “Help Desk” in the output.

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.captionOpens in a new tab. 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.booleanOpens in a new tab. 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.captionOpens in a new tab. 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:

<image id="myFrame" src="/csp/myApp/images/myPic.png" />

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:

link="javascript:zenPage.myMethod();"

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:

  • Setting a value for the <menu> layout attribute (as in the example above)

  • Choosing one of the following in place of <menu>:

    • <hmenu> — a horizontally oriented list of choices

    • <vmenu> — a vertically oriented list of choices

<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

  • key – A value that identifies the selected menu item. Available to the onselect event handler.

  • caption – Supplies text that appears in the menu item.

  • action – A value that identifies the action to take when the user selects a menu item. Available to the onselect event handler.

  • targetId – A value that identifies the object of the action. Available to the onselect event handler.

  • image – Supplies the path to an image that appears in the menu item.

  • children – An array of menu nodes that describe the submenu for a menu item.

<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:

  • The top-level of the control is initially visible.

  • The top-level is initially hidden and has to be made visible by a user event, such as pressing on an icon to make it appear.

<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:

  • level – the 0-based stack level of the navigator

  • key – the programmer-defined identifier for the current sheet. This is always ''for the initial sheet.

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:

  • title is the title that is displayed within the banner of the navigator.

  • items is an array of items to display on the sheet.

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:

  • 'select' – fire the onselect callback.

  • 'drill' – drill down one level.

  • 'link' – navigate to a new URL, value supplies the URL.

  • 'apply' – apply the value of the current item, which causes the navigator's onchange handler to fire.

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:

  • 'caption' – display the item caption

  • 'caption-value-vt' – display the caption and value laid out vertically.

  • 'caption-value-hz' – display the caption and value laid out horizontally.

  • 'value' – display the item value Note that if the caption or value are too long, they are truncated.

  • 'image-caption' – display an image and the caption

  • 'image-caption-value-vt' – display an image and the caption and value laid out vertically.

  • 'image-caption-value-hz' – display an image and the caption and value laid out horizontally.

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:

  • style – CSS style applied to the overall item (typically background).

  • captionStyle – CSS style applied to the item caption.

  • valueStyle – CSS style applied to the item value.

  • disabled – if defined and true, then this item is disabled.

  • selected – if defined and true, then display this item with the style defined for selected items.

  • checked – if defined and true, then indicate that this item is checked

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:

  • 'string' – display a simple text entry box

  • 'slider' – display a slider control. minValue and maxValue can be used to define the range.

  • 'slider-toggle' – display a slider control with a checkbox. If the checkbox is turned off the value is set to "".

  • 'stepper' – display a up/down stepper control. minValue and maxValue can be used to define the range.

  • 'stepper-value' – display a stepper with a value.

  • 'switch' – display an on/off switch.

  • 'choice' – display a small set of choices as buttons. valueList and displayList define the set of values and labels. This only works well for a small set of small choices due to the geometry of the navigator.

Perform the following steps to display an edit control:

  • Set the value of the display property to something that displays a value, 'caption-value-hz' is a good choice.

  • Set the value of the edit property to one of the available control types.

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.toolbarOpens in a new tab 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:

  • "item" – show a menu item. This is a caption that the user can click. An "item" may also define a list of children that are displayed as a drop-down menu. Clicking on an item raises the onselect event.

  • "tab" – show a "tab". This is a caption that the user can click. When the user clicks a tab, it becomes the current tab and all other tab items are unselected. Clicking on a tab raises the onselect event.

  • "message" – display a text message.

  • "choice" – display a set of choices (using a displayList and valueList) as a choice button.

  • "pages" – display a set of paging buttons, such as may be present on a search results page. In this case you can specify the minValue (first page #), maxValue (last page #) and value (current page). Clicking on a page invokes the onchangepage event.

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.TabTestOpens in a new tab in the SAMPLES namespace.

generated description: menu tab 1

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.

generated description: menu tab 2

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.booleanOpens in a new tab. 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.booleanOpens in a new tab. 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.booleanOpens in a new tab. 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.LookoutMenuTestOpens in a new tab 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>.

generated description: menu tab 3

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.booleanOpens in a new tab. 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.booleanOpens in a new tab. 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.captionOpens in a new tab. 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 (generated description: expando expand) beside the <expando> label indicates that the group is expanded (displayed), and a down-arrow graphic (generated description: expando contract) indicates that the group is contracted (hidden). A fully contracted <expando> group looks like the following example.

generated description: expando 1

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.

generated description: expando 2

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.booleanOpens in a new tab. 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.captionOpens in a new tab. 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.booleanOpens in a new tab. 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.booleanOpens in a new tab. See “Zen Attribute Data Types.”

The %ZEN.Component.expandoOpens in a new tab class offers a server-side callback method %OnDrawTitleOptions which, if defined in a subclass of %ZEN.Component.expandoOpens in a new tab, 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 (generated description: expando expand) 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 (generated description: expando contract) 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 %StringOpens in a new tab that contains the seed value from the <expando>. The callback must return a %StatusOpens in a new tab 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.booleanOpens in a new tab. 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"

generated description: dynatree

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:

  • The name of a Zen page class:

    "ZENMVC.MVCForm.cls"

  • A web site:

    "http://www.intersystems.com"

  • Or the URI of some other content.

If you want a link to invoke JavaScript, you can start the URI with the string javascript: as in the following examples:

  • You can invoke a client-side JavaScript method in the page class:

    "javascript: zenPage.myMethod();"

  • Or simply execute a JavaScript expression:

    "javascript: alert('You clicked me!');"

    When providing a JavaScript expression, use double quotes to enclose the value and single quotes (if needed) inside the expression, as shown above.

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:

  • OnGetNodeInfo” to get data for each node within the tree.

  • OnGetTreeInfo” to fill in a local array that supplies the contents of the tree.

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:

  • A tree contains a set of top-level nodes at level 1. A node may contain child nodes; each child is considered to have a level number one greater than its parent: 2, 3, 4, etc.

  • Starting with level 1, Zen calls the OnGetNodeInfo callback repeatedly until it returns false, indicating that there are no more nodes at the current level.

  • If a node reports that it has child nodes, Zen calls the OnGetNodeInfo callback repeatedly (with the appropriate value for pLevel) to get information on each child node until false is returned for that child level.

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:

  • The method returns a %BooleanOpens in a new tab value indicating whether or not there is a node at the level indicated by pLevel.

  • tSC is a status code, returned by reference, that indicates success.

  • pParms represents any <parameter> elements defined by the <dynaTree>. pParms is an array. Each member of this array uses its paramName as a subscript and its value as a value.

  • pLevel is the current level of the tree.

  • pHandle is user-definable value that is passed, unchanged, to each call to the callback. The callback can use this store any state information it needs while providing the view information.

  • pNodeInfo is a pre-instantiated %ZEN.Auxiliary.NodeInfoOpens in a new tab object and is used to specify how the tree node should be displayed. A %ZEN.Auxiliary.NodeInfoOpens in a new tab object has the following properties.

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.booleanOpens in a new tab. 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.booleanOpens in a new tab. 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:

link="javascript:zenPage.myMethod();"

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 method returns a status code.

  • pParms represents any <parameter> elements defined by the <dynaTree>. pParms is an array. Each member of this array uses its paramName as a subscript and its value as a value.

  • pRoot is the display name of the node to be loaded.

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.DynaTreeTestOpens in a new tab 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.dynaTreeOpens in a new tab 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.

generated description: dynatree show lines

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:

dataGlobal="^myTree"

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 generated description: expando expand 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 generated description: expando contract 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 generated description: dynatree folderclosed 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 generated description: dynatree folderopen 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 generated description: dynatree node 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.booleanOpens in a new tab. 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.dynaTreeOpens in a new tab programmatically, you must also know about the following properties of the dynaTree class:

  • Each <parameter> element provided in the original <dynaTree> definition in XData Contents becomes a member of the dynaTree parameters property, a list collection of %ZEN.Auxiliary.parameterOpens in a new tab objects. Each <parameter> acquires an ordinal position in the parameters collection: 1, 2, 3, etc.

  • The read-only text property holds the text (display) value of the currently selected node within the tree. This is the node label that displays in the <dynaTree>.

  • The read-only value property holds the logical (actual) value of the currently selected node within the tree. This is the string that is activated when the user clicks the corresponding label in the <dynaTree>.

<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:

generated description: buttonview

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.

generated description: buttonview 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:

category:button;

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:

category:button;category:button;category:button;

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 %StatusOpens in a new tab value, and it must have two input parameters in the following order:

  • A %StringOpens in a new tab. If you provide a seed value for the <buttonView>, it is automatically passed to the OnGetButtonInfo callback method.

  • An array passed by reference. This array contains an array of button descriptions when the method returns.

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:

  • $LB is shorthand for the ObjectScript function $LISTBUILD.

  • n is the 1–based button number.

  • category is the name assigned to the category that contains this button.

    You must group buttons in the <buttonView> into categories. All buttons with the same category value work together, like a set of radio buttons. When one button in a category is selected, none of the other buttons in that category can be selected.

    This feature is visible in the figure at the beginning of this topic, for which all of the buttons that correspond to programming languages have had their category set to the same string. The user has clicked one of these buttons (JavaScript) so all of the other buttons in this category are disabled. This action has not affected any of the buttons in other categories.

  • caption is the text that appears on the face of the button. You can use a $$$Text macro to support localization of the caption.

  • button is part of the string value that is recorded when this button is selected. Each entry in the value string for the <buttonView> component takes the following form:

    category:button;

    If the user makes a selection in more than one category, Zen appends each subsequent choice to the existing value string, as in:

    category:button;category:button;category:button;

  • tooltip is the text that displays when the user hovers the cursor over the button. The tooltip can also use $$$Text.

  • disabled, if true, disables this button. The button still displays, but is grayed out.

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 %StringOpens in a new tab as input and return a %StatusOpens in a new tab 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.

FeedbackOpens in a new tab