Skip to main content

Zen Pages

Pages within a Zen application are derived from the class %ZEN.Component.pageOpens in a new tab. A page class defines and serves the contents of a Zen page in a browser. This chapter explores Zen page programming. It begins from the foundation provided by:

This chapter provides technical details to support the information listed above. Topics in this chapter include:

Important:

A complex Zen page requires a long serial state header string to be sent with each request. Note that there is a fixed 32k limit to the length of this header string, resulting in a message like the following:

"An error has occured on the server: ERROR #5001: Serial State Header is too large"

ZenMethods that are class methods do not require a serial state header, so you may be able to avoid the limit by using class methods. However, if a Zen method needs to use the %page variable, that method must be an instance method, because %page is not available in class methods.

Zen Page Contents

The visible contents of a page are defined by a set of Zen components that belong to the page. This is the component object model for the page. To review background information and diagrams that illustrate the component object model and its relationship to the page, see the “Zen Application Concepts” chapter in the book Using Zen.

The sections in this topic explain how to specify the component object model for a Zen page. The following list of techniques contains links to the sections that provide details. You can specify Zen page contents as follows:

  • Defining Page Contents Statically Using XML You can provide an XML block named XData Contents in the Zen page class.

  • Modifying Page Contents Prior to Display You can override the %OnAfterCreatePage() callback method so that it invokes server-side methods that retrieve pointers to components, add components, modify components, hide or redisplay components, and remove components from the component object model.

  • Modifying Page Contents after Display. After the page has displayed, set up handlers for user actions (such as clicks) so that they invoke client-side or server-side methods that retrieve pointers to components, add components, modify components, hide or redisplay components, or remove components from the component object model.

You can also override the onloadHandler() client-side method to modify the page before it displays on the client side. This JavaScript method runs on the client side just prior to displaying the page. See the entry for onloadHandler() in the table in section “Client Side Callback Methods for the Page” for more information.

You can define the basic page using XML, then use any combination of the above techniques to modify it dynamically at runtime.

Defining Page Contents Statically Using XML

A Zen page class can provide an XData Contents block (an XML document embedded within the class definition) that defines the set of components used for the page. It is possible to define the contents of a page programmatically, but in most cases an XML block is more convenient.

The following XData Contents block defines a simple page object containing a single button component:

XData Contents [XMLNamespace="http://www.intersystems.com/zen"]
{
<page xmlns="http://www.intersystems.com/zen" title="My Page">
  <button caption="Hey"/>
</page>
}

The XData Contents block is not processed at runtime. Instead, at compile time the class compiler uses the XData Contents information to generate a method called %CreatePage. You can view (but not modify) this method if you like by compiling a page class and then using the View Other command in Studio to see the code generated for the page class. The %CreatePage method is called at runtime to create the contents of the page component model.

For a detailed description of XData Contents, see the chapters “Zen Layout” and “Zen Style” in the book Using Zen.

Note:

InterSystems recommends that you include an XML namespace declaration within an XData Contents definition. You do this by providing the xmlns attribute with the <page> element. For example:

<page xmlns="http://www.intersystems.com/zen" >

As you add custom components, chances increase for conflicts between components in different namespaces. This is also true for other Zen elements such as <composite> and <pane>. Adding the xmlns attribute to the <page> prevents namespace conflicts.

Modifying Page Contents Prior to Display

If you need to programmatically modify the contents of a page before displaying it, add code to the %OnAfterCreatePage() callback method as in the following example. This example creates a new group component, then adds the group to the page using the %AddChild() method of the page. Subsequently, it creates several components, sets their properties, and adds them to the group using %AddChild() method of the group:

Method %OnAfterCreatePage() As %Status
{
  Set tGroup = ##class(%ZEN.Component.group).%New()
  Do %page.%AddChild(tGroup)

  Set tBtn = ##class(%ZEN.Component.button).%New()
  Set tBtn.caption = "Button 1"
  Do tGroup.%AddChild(tBtn)

  Set tBtn = ##class(%ZEN.Component.button).%New()
  Set tBtn.caption = "Button 2"
  Do tGroup.%AddChild(tBtn)

  Quit $$$OK
}

Any text you add for captions or other labels is not automatically localized as it is when you add a caption attribute using XML. If you want localized captions, your %OnAfterCreatePage() method must call the $$$Text localization macros. This is not shown in the example above.

If the component is not visible (such as <timer>) you may add it to a page using %AddComponent() instead of %AddChild(). For components that are visible, the methods %AddChildBefore() and %AddChildAfter() are also available. These methods have a second parameter that identifies the existing, sibling component.

One of the best ways to get a sense of how pages are put together programmatically is to look at the generated code for the %CreatePage method in any Zen page class that uses XData Contents to define page contents. To do this:

  1. Open the page class in Studio.

  2. Choose View > View Other or Ctrl-Shift-V or the generated description: studio view other icon.

  3. Select the file marked “1” and click Open.

  4. Search for this string: %CreatePage

  5. Scroll through the method to see how components are added to the page.

The following table lists the server-side methods that allow you to dynamically manipulate the component object model for a page. These methods are final and you cannot override them. All group components, including the page, support these methods. You can invoke these methods on a group or page object at any hierarchical level in the component object model.

Page and Group Server Side Methods for Dynamic Page Contents
Method Description
%AddChild(obj)

Add a child component to this page or group. obj is a pointer to the component you wish to add. If %AddChild() is called on a group, it adds the component to the group and also adds it to page that the group belongs to.

Before calling %AddChild() to add a component to a group:

  • The group itself must be added to the page.

  • The component object must exist; create it using %New()

  • The id property of the component must be set.

%AddChildAfter(obj,sib) Same functionality and restrictions as %AddChild(), but adds the child component obj to the group in the position immediately after the sibling object identified by sib. sib must be a pointer to an object that is already in the group.
%AddChildBefore(obj,sib) Same functionality and restrictions as %AddChild(), but adds the child component obj to the group in the position immediately before the sibling object identified by sib. sib must be a pointer to an object that is already in the group.
%AddComponent(obj) obj is a pointer to the component you wish to add to the page or group. Use %AddComponent() as an alternative to %AddChild() only when adding an invisible component, such as a <timer>. Otherwise use %AddChild().
%GetChildIndex(obj) Look for the child component obj within this group and return its 1-based index number. Return -1 if unable to find the child.
%RemoveChild(obj) Remove child component obj from this group. Returns 1 (true) if the component was found and removed.
%RemoveChildren(flag) Remove all child components from this group. If flag is 1 (true) then remove only those children that were dynamically added to the group, retaining those that were statically added from XData Contents. If flag is omitted or 0 (false) delete all child components from this group.

Modifying Page Contents after Display

If you need to modify components in a page dynamically based on user interactions on the client side, you can do this as well. The SAMPLES namespace class ZENTest.DynamicComponentsTestOpens in a new tab provides useful examples of doing this from both the client and the server sides in response to user input. The “Sample Development Project” section provides another example of dynamically adding components to a page. Remember that you need to both create the component and add it as a child of the page before the user can see it.

In order for components to display correctly on a Zen page, Zen must load the relevant CSS and JavaScript. In the interest of improving performance, Zen pages do not automatically load CSS and JavaScript for all components. Zen determines which files to load by parsing the XData Content block and methods called during display of the page, such as %OnAfterCreatePage(). When you add components to the page dynamically in a client- or server-side method, you must use the %import property to identify the components so Zen can load the required CSS and JavaScript files. Zen checks for redundancy, so if the import property specifies a file that has already been loaded, the file is not included twice.

The following code illustrates an %import property for the <svgFrame> and <lineChart> components.

Property %import As %ZEN.Datatype.string(MAXLEN = 1000, XMLNAME = "import") 
  [InitialExpression = "%ZEN.SVGComponent.svgFrame,%ZEN.SVGComponent.lineChart"];

If a page displays correctly even though you have not used %import for all dynamically added components, you should not assume that using %import is not necessary. The required CSS and JavaScript may have been included in files loaded as a result of parsing the XData Content block. However, InterSystems may change the organization of these CSS and JavaScript files at any time.

The following tables list the client-side methods that allow you to dynamically manipulate the component object model for a page. These methods are final and you cannot override them. The page supports the following client-side methods for creating and deleting component objects.

Page Client Side Methods for Creating Components
Method Description
createComponent(name) Create a new component object in the default zen namespace. name is the simple name of a component class, for example "button".
createComponentNS(ns,name)

Create a new component object in the ns namespace. name is the simple name of a Zen component class — for example "button" — or the full package and class name of a custom component.

When you use this method, it is important to also use one of the following conventions to ensure that Zen knows how to load the JavaScript for the custom components:

deleteComponent(obj,rfr,sync)

Given the component identified by obj, delete it from the page.

If the optional rfr (refresh) parameter is false, the page is not refreshed after the delete. This is useful if you know that subsequent code causes such a refresh. The default is to refresh.

If the optional sync (synchronize) parameter is true, the refresh of the group containing the deleted component is executed synchronously. The default is asynchronous.

All group components, including the page, support client-side methods for adding and removing component objects once they are created. You can invoke these methods on a group component at any hierarchical level in the component object model, including the page itself.

Page and Group Client Side Methods for Dynamic Page Contents
Method Description
addChild(obj,refresh)

Add a child component to this page or group. obj is a pointer to the component you wish to add. You must first create the component using createComponent() or createComponentNS(). If refresh is true, refresh the display of this group after adding the new component.

addChildAfter(obj,sib,refresh) Same as addChild(), but adds the child component obj to the group in the position immediately after the sibling object identified by sib. sib must be a pointer to an object that is already in the group. If refresh is true, refresh the display of this group after adding the new component.
addChildBefore(obj,sib,refresh) Same as addChild(), but adds the child component obj to the group in the position immediately before the sibling object identified by sib. sib must be a pointer to an object that is already in the group. If refresh is true, refresh the display of this group after adding the new component
getChildIndex(obj) Look for the child component obj within this group and return its 01-based index number. Return -1 if unable to find the child.
removeChild(obj) Remove child component obj from this group. Returns 1 (true) if the component was found and removed.

Zen Methods on Client and Server

The following table outlines the possibilities for different types of methods in a Zen page class.

Page Class Method Conventions
Callable From Runs On Class Scope Timing Code Language Keywords Naming Convention
Client Client Instance JavaScript

ClientMethod

[Language = javascript]

myMethod
Client

Server

(Can send back code that runs on the client)

Instance Async ObjectScript, Caché Basic, Caché MVBasic [ZenMethod] MyMethod
Sync
Class Async
Sync
Server Server Instance ObjectScript, Caché Basic, Caché MVBasic %MyMethod
Class

Any method defined within a page class whose name does not start with % becomes a method of the client-side page object. Any method whose name starts with % is server-only.

Any instance method defined with the keyword ClientMethod with [Language = javascript] becomes a client-side instance method of the client page object. When called, it executes within the browser.

Any method defined with its [ZenMethod] keyword set automatically becomes a client-side instance method of the client page object. When the client-side method is called, the server-side version of the method is executed. Any context needed to run the server method is automatically provided. An instance method runs as an instance method on the server; the serialized state of its client object is shipped to the server and instantiated. A class method runs as a class method on the server (but is called as an instance method on the client).

Synchronous and Asynchronous Methods

Server-side methods can be called synchronously or asynchronously. This happens very smoothly and automatically as follows: If a server-side method has a return type, it is called synchronously, and its return value is returned to the client. If the method has no return type, it is called asynchronously; the client does not wait for the return value.

The CSP hyperevent logic handles all the details of this, so (unlike AJAX) you do not need to provide any additional application logic to enable asynchronous calls. In this way, Zen takes the next step past most AJAX frameworks.

The main advantages of asynchronous methods are:

  • The browser can keep working while the server request is processed

  • You can call a server method, modify the client (say by displaying a message), and the server logic can update this message when it completes

Some of the Zen components take advantage of asynchronous methods. For example, the <dataCombo> control uses an asynchronous method to execute a server-side query. This allows the <dataCombo> to display a loading... message while the query is running. This message is replaced when the response from the server arrives, so the user does not see the message if the query is quick.

Zen offers a client-side flag zenSynchronousMode that you can set to true to force all methods to be executed synchronously, regardless of their return values. When false, methods are synchronous if they have a return value, asynchronous otherwise. When you want to set this flag:

  1. First, get and store the current value of zenSynchronousMode.

  2. Then, set the flag as you wish.

  3. When your processing is done, restore the previous value of zenSynchronousMode.

In addition to this convention, the client-side refreshContents method (available for every component) offers a single optional argument whose value can be true or false. This flag specifies whether the client should refresh the component synchronously (true) or asynchronously (false). If this argument is omitted, the default is false.

Collections as Method Arguments and Return Types

It is possible to pass a collection between the client and server using a Zen class method. This section explains the conventions for doing so. Both lists and arrays are supported. For an example using lists, see the section “Using a JavaScript Method” in the “Zen Charts” chapter of Using Zen Components.

Important:

For the conventions described in this topic to work, the [ZenMethod] must be a class method, declared using the ClassMethod keyword as shown in the examples. Collections cannot be arguments or return types for instance methods. Also, only collections of literals are supported this way. If an application needs to pass something more complex, it must define a Zen object class and use that instead.

Lists

To pass a list collection as an argument to a server-side Zen class method, use %ListOfDataTypesOpens in a new tab as the data type, as shown here:

ClassMethod MyMethod(list as %Library.ListOfDataTypes) [ZenMethod]
{
  Set x = list.GetAt(1)
}

From the client, you can call this method by passing it a JavaScript Array object:

 var list = new Array();
 list[list.length] = 22;
 zenPage.MyMethod(list); 

To return a list from the server, use %ListOfDataTypesOpens in a new tab as the return value for the server-side Zen class method:

ClassMethod MyMethod() as %Library.ListOfDataTypes [ZenMethod]
{
  Set ret = ##class(%Library.ListOfDataTypes).%New()
  Do ret.Insert("red")
  Quit ret
}

From the client, this method returns a JavaScript Array object, which is 0–indexed. For example:

var list = zenPage.MyMethod();
alert(list[0]); // should show 'red' 

Arrays

To pass an array collection as an argument to a server-side Zen class method, use %ArrayOfDataTypesOpens in a new tab as the data type, as shown here:

ClassMethod MyMethod(arg as %Library.ArrayOfDataTypes) [ZenMethod]
{
  Set x = arg.GetAt("item1")
}

From the client, you can call this method by passing it a JavaScript associative array (an Object in JavaScript). For example:

var arr = new Object();
arr['item1'] = 22;
zenPage.MyMethod(arr); 

To return an array from the server, use %ArrayOfDataTypesOpens in a new tab as the return value for the server-side Zen class method:

ClassMethod MyMethod() as %Library.ArrayOfDataTypes [ZenMethod]
{
  Set ret = ##class(%Library.ArrayOfDataTypes).%New()
  Do ret.SetAt("red","color")
  Quit ret
}

From the client, this method returns a JavaScript associative array (an Object in JavaScript). For example:

var arr = zenPage.MyMethod();
alert(arr['color']); // should show 'red'  

Example: Passing Collections Between Client and Server

The following Zen page class example shows the code conventions required for the following behavior:

  • Clicking the top button sends a collection object from client to server.

  • Clicking the bottom button retrieves a collection object from the server and sends it to the client.

Class MyApp.Test Extends %ZEN.Component.page
{
  XData Contents [XMLNamespace = "http://www.intersystems.com/zen"]
  {
    <page xmlns="http://www.intersystems.com/zen" title= "">
      <button caption="Ship array to server" 
              onclick="zenPage.testClientToServer();" />
      <button caption="Get array from server" 
              onclick="zenPage.testServerToClient();" />
    </page>
  }
  
  ClientMethod testClientToServer() [ Language = javascript]
  {
    var list = new Array();
    list[list.length] = 22;
    alert(zenPage.MyClientToServer(list));
  }
  
  ClassMethod MyClientToServer(pList As %ListOfDataTypes) As %String [ZenMethod]
  {
    Set ^%x = pList.GetAt(1)
    Quit ^%x
  }
  
  ClientMethod testServerToClient() [ Language = javascript]
  {
    var list = zenPage.MyServerToClient();
    alert(list.join('\n'));
  }
  
  ClassMethod MyServerToClient() As %ListOfDataTypes [ZenMethod]
  {
    Set pList = ##class(%ListOfDataTypes).%New()
    Do pList. Insert("RED")
    Do pList. Insert("GREEN")
    Do pList. Insert("BLOOP")
    Quit pList
  }
}

Embedded HTML and JavaScript

The following sample %DrawHTML method uses a syntax that you see commonly throughout the Zen documentation and sample code: The keyword &html followed by HTML statements enclosed in angle brackets <>. This is ObjectScript syntax for embedded HTML statements:

Method %DrawHTML()
{
    &html<<div class="MyComponent">Message</div>>
}

ObjectScript offers a similar syntax for embedded JavaScript statements in server-side methods marked with the [ZenMethod] keyword. Within these methods, the keyword &js can be followed by JavaScript statements enclosed in angle brackets <>. While executing the method on the server, Zen finds the snippet of JavaScript code and sends this snippet to the client for execution. Note that you must escape any angle brackets used inside the brackets that enclose the embedded JavaScript. For example:

Method BtnClickMe() [ZenMethod]
{
   &js<alert('Client Method');>
   Quit
}

In addition to &html<> and &js<>, there is also an &sql<> convention for SQL statements. Each of these ObjectScript language conventions offers a useful shortcut for enclosing snippets of code within Zen application and page classes. For syntax details, see the definition of the & symbol in the Caché ObjectScript Reference.

Important:

The “&js< ... >” should ONLY be used when working in Synchronous mode and interacting with ClassMethods where there is no Document Object Model (DOM) synchronization happening.

In an instance method, if you are modifying elements in the DOM, this code is returned by the hyperevent, bundled in to a function and executed in the browser immediately on return; then the DOM then happens, overwriting any and all changes made by the function. If you are calling Asynchronously, this is also a risk that these functions may not execute in the order you expect them to.

Automatic Type Conversion for Server Side Methods

The mechanism for handling type conversions when invoking a server-side method works as follows:

  • When calling a server-side method, any %ZEN.Datatype.booleanOpens in a new tab arguments are converted from client-side true/false to server-side 1/0.

  • If a server method has a %ZEN.Datatype.booleanOpens in a new tab return type, its return value are converted to a client-side true/false value.

  • If a server method has a %ZEN.Datatype.listOpens in a new tab return type, its return value are converted to a client-side array.

  • If a server method has a numeric return type, its return value are converted to a client-side numeric value (unless it the method returns "").

  • Methods that take object-valued arguments behave well if the argument is missing or null.

Server Side Methods

The topics in this section describe important page and component methods that run on the server side.

Server Side Callback Methods for the Page

Callback methods are user-written methods within a Caché class that are triggered, not by a method call, but by specific system events. To distinguish callback methods, Caché developers give them names of the following form:

%OnEvent

where the keyword Event identifies the Caché system event that triggers the callback. If an event supports a callback method, the method controlling the event automatically executes the callback method, if it exists within the class.

You must never execute callback methods by calling them explicitly. Instead, provide an implementation of the method within your class so that Caché can invoke the method automatically when the time is right. If you want your class to do nothing in response to an event, omit any implementation or override of the corresponding callback method.

When an HTML page derived from %ZEN.Component.pageOpens in a new tab is drawn, it invokes a number of callback methods that are executed on the server before the page content is sent to the browser. The following table lists these methods. A page class can override these methods to control the behavior of the page.

Page Class Server Side Callback Methods
Callback Method Description
%OnBeforeCreatePage() This class method is called just before the server-side page object is created.
%OnCreatePage() This instance method is called just after the server-side page object is created but before any of its child components are created.
%OnAfterCreatePage() This instance method is called after the server-side page object and all of its predefined child components are created. A subclass can override this method to add, remove, or modify items within the page object model, or to provide values for controls.
%OnDrawHTMLHead() This instance method is called when the HTML for the page is being rendered just before the end of the HTML HEAD section of the page. This provides a way to inject additional content into the HEAD section of a page should this ever be necessary.
%OnDrawHTMLBody() This instance method is called when the HTML for the page is being rendered close to the start of the HTML BODY section of the page (before any child components are rendered). This provides a way to inject additional content into the BODY section of a page should this ever be necessary.
%OnMonitorBackgroundTask(...) This class method is called when the client checks on the progress of a background task and the task is still running. For details, see the section “Running Background Tasks on the Server.”
%OnFinishBackgroundTask(...) This class method is called when the client checks on the progress of a background task and the task is complete. For details, see the section “Running Background Tasks on the Server.”

Server Side Methods for the Page

The page class contains a number of server-side methods that can be useful when working with the component object model on the server, running background tasks on the server, or processing form submits. These methods are final and you cannot override them.

The following table lists the general purpose server-side methods. For a list of the server-side page class methods that allow you to modify the component object model, see the section “Defining Page Contents Dynamically on the Server.”

Page Class Server Side Methods
Method Description
%GetComponent(index) Return a pointer to the component object, given its system-assigned index number index.
%GetComponentById(id) Return a pointer to the component object, given its user-assigned id (the id value assigned to it in XData Contents or other user code).
%GetComponentByName(name) Return a pointer to the component object, given its user-assigned name (the name value assigned to it in XData Contents or other user code). If multiple components have the same name on this page, return the first one found.
%GetValueById(id) Return the value of a control component, given its user-assigned id.
%GetValueByName(name) Return the value of a control component, given its user-assigned name. If multiple components have the same name on this page, return the first one found.
%EndBackgroundMethod() Clean up after a method that was invoked as a background task. For details, see the section “Running Background Tasks on the Server.”
%RunBackgroundMethod(...) Invoke a method as a background task. The method runs in the background but the Zen page does not wait for the method to return. For details, see the section “Running Background Tasks on the Server.”
%SetBackgroundMethodStatus(...) Can be called from within a method that is running in the background, to update its own status information. For details, see the section “Running Background Tasks on the Server.”
%SetErrorById(id, msg) Set the error message for a component, given the id assigned to this component in XData Contents or other user code. msg is the error message.
%SetErrorByName(name, msg) Set the error message for a component, given its user-assigned name. msg is the error message.
%SetValueById(id) Set the value of a control component, given its user-assigned id.
%SetValueByName(name) Set the value of a control component, given its user-assigned name.
%SetValuesByName(array) Set the values of a set of control components, given an array of values subscripted by the user-assigned name values of each control component in the set.

Running Background Tasks on the Server

There is a mechanism for starting a background job from a Zen page and tracking its progress within the page. The method runs in the background but the Zen page does not wait for the method to return before allowing the user to continue to interact with the Zen application. This can be useful for cases where a page needs to initiate a long-running task and wants to provide some degree of feedback to the client while waiting for that task to complete. Running a background task prevents a premature timeout of the Zen page, since the maximum configurable timeout for a Zen page may be less than the time required for some long-running tasks to complete. You must have a product license installed in order to use this Zen feature.

A Zen page class has a backgroundTimerInterval property. This is the interval, in milliseconds, at which timer events are fired to check on the status of background tasks started by this page. The default is 1000.

The following table describes the server-side methods that support background tasks; these are available in any class that inherits from %ZEN.Component.pageOpens in a new tab.

Page Class Server Side Methods for Background Tasks
Method Description
%EndBackgroundMethod()

Clean up after a method that was invoked as a background task. This deletes any status information that was being maintained for the background task.

This method is final. You can cannot override it.

%OnFinishBackgroundTask(id)

You can override this server-side callback method to control the behavior of the page when the client checks on the progress of a background task and the task is complete. Typically you would use this method to send back JavaScript to update the page, for example with a completion message.

%OnMonitorBackgroundTask(id,status,val)

You can override this server-side callback method to control the behavior of the page each time the client checks on the progress of a background task and the task is still running. Typically you would use this method to send back JavaScript to update the progress bar on the client.

The following arguments are automatically provided to the callback when it is invoked: id is a string giving the job ID for the background task, status is the current job status as a string, and val is a %FloatOpens in a new tab value that indicates how much of the background task is complete (as a percentage value between 0 and 100).

%RunBackgroundMethod(method,arg1,arg2,...)

Starts a background job to run a class method of this Zen page. %RunBackgroundMethod() returns a %StatusOpens in a new tab value and may have one or more arguments:

  • method gives the name of the class method to run.

  • Depending on the method signature, zero or more arguments may follow — arg1, arg2, and so forth — these are the arguments of the method named in the first argument.

Zen monitors only one background task at a time. If %RunBackgroundMethod() is called while a previous background task is running, the new method becomes the current monitored task. The previous task runs to completion, but the client is not notified about it.

This method is final. You can cannot override it.

%SetBackgroundMethodStatus(msg,val)

Can be called from within a method that is running in the background, to update its own status information. %SetBackgroundMethodStatus has no return value and takes two arguments:

  • A %StringOpens in a new tab specifies the status message seen by the client page. The string may be empty.

  • An optional %FloatOpens in a new tab value indicates how much of the background task is complete (as a percentage value between 0 and 100). A client page can use this information to display progress to the user.

This method is final. You can cannot override it.

Server Side Callback Methods for Components and Pages

The following table lists and describes server side callback methods supported by components (including the page itself). These are methods which, if defined, are called in response to specific events. These methods are available for you to implement or override, for example if you are writing custom components.

Component and Page Server Side Callback Methods
Callback Method Description
%OnDrawEnclosingDiv() This server-side callback method must return a string that contains additional attributes for the HTML <div> element that encloses this component on the display page. Be sure to start the returned string with a space character to prevent the new %OnDrawEnclosingDiv() attributes from conflicting with other attributes of the same <div>.

Client Side Methods

The topics in this section describe important page and component methods that run on the client side.

Client Side Callback Methods for the Page

The client-side page object zenPage inherits a number of callback methods; the following table lists and describes them. These are JavaScript methods which, if defined, are called in response to specific events. These methods are available for you to implement or override, for example if you are writing custom components.

Page Class Client Side Callback Methods
Callback Method Description
onkeydownHandler(evt) This client-side page method, if present, is fired when a keydown event occurs on the page. evt is a zenEvent object, a direct analog for the DOM window.event data structure. For details, see zenEvent in the section “Client Side Functions, Variables, and Objects.”
onkeyupHandler(evt) This client-side page method, if present, is fired when a keyup event occurs on the page. evt is a zenEvent object, a direct analog for the DOM window.event data structure. For details, see zenEvent in the section “Client Side Functions, Variables, and Objects.”
onlayoutHandler(load) This client-side page method, if defined, is called when the page is first loaded or whenever it is resized. If this is called at load time, then load is true; otherwise it is false.
onloadHandler() This client-side page method, if defined, is called when the client page is finished loading. It is triggered by the page’s HTML onload event.
onlogoutHandler()

If this client-side page method is defined and the AUTOLOGOUT parameter for this page is set to 1 (true), this method is invoked when the logout timer for this page fires. Subsequent behavior depends on the return value of this method.

  • If this method returns true, the normal page logout behavior fires. That is, the page is reloaded, causing a login page to appear if the current session has ended.

  • If this method returns false, Zen does whatever this method specifies and then suppresses the normal page logout behavior.

onoverlayHandler(index) This client-side page method, if present, is fired when a component with an overlay is clicked on. The index value is the system-assigned index number used internally to identify this component. For components within repeating groups, index includes a dot followed by a number indicating the (1–based) position of this component within the group. Applications can use but should not set the index value.
onPopupAction(...) This client-side component method, if present, is fired when a Zen popup window has specified this page as its parent and subsequently fires an action. The page is the default parent of a popup window that has no parent component specified. See “Popup Windows” in the “Popup Windows and Dialogs” chapter of Using Zen Components.
onresizeHandler() This client-side page method, if defined, is called when the client page is resized. It is triggered by the page’s HTML onresize event.
onServerMethodCall(method) If implemented, this client-side page method is called just before a server method in the page class is invoked. method is the method name. For details and examples, see “Notifying the Client When Server Side Methods Run.”
onServerMethodError(err) If implemented, this client-side page method is called whenever a server method call returns an error from the server. err contains the error message. For details and examples, see “Notifying the Client When Server Side Methods Run.”
onServerMethodReturn(method) If implemented, this client-side page method is called just after a server method in the page class is processed. method is the method name. For details and examples, see “Notifying the Client When Server Side Methods Run.”
onunloadHandler()

This client-side page method, if defined, is called when the client page is about to unload. It is triggered by the page’s HTML onbeforeunload event.

If the onunloadHandler method returns a string value, a confirmation dialog appears whenever the user attempts to navigate away from or refresh the page. The dialog displays the string that you specify as the return value of the onunloadHandler method.

If the onunloadHandler method is not present, has no return statement, has no return value, returns true, returns false, or returns any non-string value, then no confirmation dialog appears when the user leaves or refreshes the page.

Notifying the Client When Server Side Methods Run

A Zen page can be notified before and after a server method is invoked. To be notified before a server method is called, a Zen page class should implement the abstract method onServerMethodCall, a client-side JavaScript method. For example:


ClientMethod onServerMethodCall(method) [ Language = javascript ]
{
    alert('Call: ' + method);
}

To be notified just after a server method returns code to the client, a Zen page should implement the abstract method onServerMethodReturn, a client-side JavaScript method. For example:


ClientMethod onServerMethodReturn(method) [ Language = javascript ]
{
    alert('Return: ' + method);
}

To be notified when a server method returns an error, a Zen page should implement the abstract method onServerMethodError, a client-side JavaScript method. For example:


ClientMethod onServerMethodError(error) [ Language = javascript ]
{
    alert('Return: ' + error);
}

Client Side Methods for the Page

The client-side page object zenPage inherits a number of JavaScript methods that can be useful in client-side programming. Unlike the methods described in the previous section, “Client Side Page Callback Methods,” these methods are final and cannot be overridden.

The following table lists the general purpose client-side methods. For a list of the client-side page class methods that allow you to modify the component object model, see the section “Defining Page Contents Dynamically on the Client.”

Page Class Client Side Methods
Method Description
cancelPopup() Close the current popup window with no further action. See “Popup Windows” in the “Popup Windows and Dialogs” chapter of Using Zen Components.
endModal() Hide the current modal group, if there is one. See “Modal Groups” in the “Popup Windows and Dialogs” chapter of Using Zen Components.
fireOnLoadEvent() Fire the onload event for every component on the page that defines one. Zen fires the events in reverse order according to the internal hierarchy of the page, so that it fires the page’s onload event last.
fireOnResizeEvent() Fire the onresize event for the page, if it defines one.
fireOnUnloadEvent() Fire the onunload event for every component on the page that defines one. If any component's onunload handler returns a string value, Zen uses that as the return value of the page's onbeforeunload handler.
firePopupAction(...) Notify the parent window of this popup that a user action has occurred. For full information, see “Popup Windows” in the “Popup Windows and Dialogs” chapter of Using Zen Components.
getComponent(index) Find a component on the page given its index number, a system-assigned number used internally to identify this component. For components within repeating groups, index includes a dot followed by a number indicating the (1–based) position of this component within the group. Applications can use but should not set the index value.
getComponentById(id,tuple)

Find a component on the page given its id value. For components within repeating groups, the optional tuple number indicates the (1–based) position of this component within the group.

The zen JavaScript function is a shorthand equivalent for getComponentById() when there is no tuple involved. You can use:

zen(id)

In place of:

zenPage.getComponentById(id)

gotoPage(url) Set the location of the browser to the specified new URI. Use gotoPage to navigate to new pages to ensure that URIs are encoded correctly.
launchPopupWindow(...) Launch a popup window. For full information, see “Popup Windows” in the “Popup Windows and Dialogs” chapter of Using Zen Components.
setComponentId(obj,id) Given the component identified by obj, change its id value.
setTraceOption(name,flag) This client method lets you turn the client-side tracing flags on or off. name is the name of the tracing option. The name value can be 'events' (trace client events), 'js' (display the JavaScript returned from the server), or 'serialize' (display object serializations). flag is true to enable the specified option, false to disable it.
startModal(obj) Make a component visible as a modal group. Alternately, use the show method of the <modalGroup> component. See “Modal Groups” in the “Popup Windows and Dialogs” chapter of Using Zen Components.

Client Side Callback Methods for Components and Pages

Components (including the page itself) inherit a number of callback methods; the following table lists and describes them. These are JavaScript methods which, if defined, are called in response to specific events. These methods are available for you to implement or override, for example if you are writing custom components.

Component and Page Client Side Callback Methods
Callback Method Description
onPopupAction(...)

If present, fires when a Zen popup window has specified this component as its parent and subsequently fires an action.

For details, see “Popup Windows” in the “Popup Windows and Dialogs” chapter of Using Zen Components.

onEndModalHandler(zindex) If present, fires upon notification that this component is about to stop being modal. That is, it is no longer pushed to the front of the display. The caller supplies a zindex value small enough to ensure that this component returns to its normal layer relative to other components in the browser. For details, see “Modal Groups” in the “Popup Windows and Dialogs” chapter of Using Zen Components.
onRefreshContents() If present, this callback is invoked by the refreshContents method just after the new HTML is delivered to it from the server.
onStartModalHandler(zindex)

If present, fires upon notification that this component is about to become modal. That is, it is be pushed to the front of the display. The caller supplies a zindex value large enough to ensure that this component is placed above all others currently visible in the browser.

For details, see “Modal Groups” in the “Popup Windows and Dialogs” chapter of Using Zen Components.

Client Side Methods for Components and Pages

Every component on the page (including the page itself) inherits a number of useful JavaScript methods. The following table lists these methods. Specific components also have specialized methods not listed in the table. Unlike the methods described in the previous section, “Client Side Page Callback Methods,” these methods are final and cannot be overridden. You can call them from your page class or custom component class as specified here.

Component and Page Client Side Methods
Method Description
findElement(id) Get the HTML element associated with this component. id is the id value that you supplied for this component in the Zen page class.
getEnclosingDiv() Get the HTML div element used to enclose a specific component.
getLabelElement() Get the HTML element that displays the label for this component.
getHidden() Return true if a component is currently hidden, otherwise false.
getHintElement() Get the HTML element that displays the hint text for this component.
getProperty(name, key) Return the value of the component property identified by name. The optional parameter key is useful when properties (such as collections) need a key to find a specific value.
getSettings(settings) Get the set of named properties for this component. getSettings returns a list of property names in the associative array that is the argument for the method (settings). You can then retrieve and modify property values using these property names as arguments to getProperty and setProperty.
getValue() (Subclasses of control only). Get the value of a control. This is equivalent to calling getProperty('value') on this component.
invokeSuper(method, args) Invoke the super class implementation of the identified method. Supply the arguments for the method as a JavaScript array in args.
isOfType(type) Test if a component is a subclass of (is a type of) another component. For the input argument type, use the simple name of a component class, such as form or control.
refreshContents(flag)

Make a request to the server to regenerate the HTML that defines this component. If flag is supplied and is set to true, and if deferred mode is not in effect, refresh the component synchronously.

refreshContents has the effect of redisplaying the component on the browser page. For a <tablePane> component, use executeQuery() instead.

setHidden(flag) If flag is supplied and is set to true, sets the component to be hidden and invokes the onhide callback for this component. If flag is supplied and is set to false, sets the component to be displayed and invokes the onshow callback.
setProperty(name, val) Set the value of a property for a component. name gives the property name. val gives the value.
setPropertyAll(name, val) Set the value of a property for a group component and all of its child components. name gives the property name. val gives the value. setPropertyAll() does not work for the disabled property; use setProperty() instead.
setValue(val) (Subclasses of control only). Set the value of a control. This is equivalent to calling setProperty('value',val) on this component.
startProgressBar(div) Start the display of a progress bar within the display area for this component. This can be useful for components that refresh their values from the server. If div is provided it is the enclosing HTML div that contains the progress bar.
stopProgressBar() Stop the timer used by the progress bar.

Zen Properties on Client and Server

Any property defined within a Zen page class whose name does not start with % also becomes a property of the client-side page object. These client-side properties are initialized to the values they held on the server side, immediately before the page’s %DrawHTML method was invoked.

If you want to provide values for these page properties before the page displays, you can provide initial values for page class properties in one of the following ways:

  • Define an InitialExpression for the property in the page class definition, for example:

    Property dayList As %ZEN.Datatype.caption
      [ InitialExpression = "Sun,Mon,Tue,Wed,Thu,Fri,Sat" ];
    
  • Set the property in the %OnCreatePage or %OnAfterCreatePage callback methods.

In server-side methods, you can get and set values of class properties directly, using normal dot syntax. For example:

Method serverInstanceMethodCreate() [ ZenMethod ]
{
  #; create
  Set group = %page.%GetComponentById("group")
  If '$IsObject(group) {
    &js<alert('Unable to find group.');>
    Quit
  }
  Set count = group.children.Count()

  Set cal = ##class(%ZEN.Component.calendar).%New()
  Set cal.id = "NewCal"_count
  Do group.%AddChild(cal)

  Set btn = ##class(%ZEN.Component.button).%New()
  Set btn.id = "NewBtn"_count
  Set btn.caption = "New Button"
  Do group.%AddChild(btn)

  Set btn = ##class(%ZEN.Component.text).%New()
  Set btn.label = "Hey"
  Do group.%AddChild(btn)
}

To ensure proper synchronization of values between client and server, you should not access property values directly in client-side methods. You must use the get and set methods provided in the component classes. For single-valued properties, use the getProperty and setProperty methods. For example:

var index = table.getProperty('selectedIndex');

Or:

var svg = zen('svgFrame');
svg.setProperty('layout','');
svg.setProperty('editMode','drag');
Important:

Do not confuse getProperty and setProperty with the getValue and setValue methods, which get and set the value of a control component, and do not apply to any other component property.

There is a limitation on the values that you can assign to the properties of Zen components. You cannot use values in the DOM that include any of the ASCII characters with numeric values 0 through 10. These characters are usually expressed in ObjectScript code using the $CHAR ($C) function with a numeric value, for example $C(1) or $C(4). These special characters are reserved for use as delimiters within the Zen serialization code. Therefore, if you use any of the ASCII characters 0 through 10 in the values of ZEN component properties, these values cause errors when Zen deserializes the page.

Note that multidimensional properties are not supported as standard Zen page properties. You can achieve the same result using lists or arrays, or via JSON.

Zen Page Class Parameters

There are a number of class parameters that a page class can use to control the behavior of the page. This section lists the most useful of them. Others are described in the online class documentation.

APPLICATION

Package and class name of the associated application.

AUTOLOGOUT

If 1 (true) attempt to refresh this page when its session has expired. If 0 (false) do not. The default is 1.

If AUTOLOGOUT is 1, a Zen page generates JavaScript to set up a timer that reloads the page at about the time when the server session expires. It also generates code so that the client automatic logout timer is reset on every call to the server (the server already resets the session timeout). If AUTOLOGOUT is 0, then none of this code is generated.

If the onlogoutHandler client-side page method is defined and AUTOLOGOUT is set to 1 (true), onlogoutHandler is invoked each time the logout timer for this page fires. Subsequent behavior depends on the return value of the method, as follows:

  • If this method returns true, the normal page logout behavior fires. That is, the page is reloaded, causing a login page to appear if the current session has ended.

  • If this method returns false, Zen does whatever this method specifies and then suppresses the normal page logout behavior.

CSSINCLUDES

Comma-separated list of Cascading Style Sheet (.css) files for the page. You can use URLs or simple file names. If you use simple file names, the CSP Files Physical Path specifies the physical path to these files. For further information, see the section “Zen Application Configuration.” For information about style sheets, see the “Zen Style” chapter in the book Using Zen.

DOMAIN

You must provide a value for this parameter if you wish to use Zen localization. The default is "" (an empty string indicating no domain).

HTMLATTRS

Zen appends this string to the generated HTML tags at the beginning of the output page, as a set of whitespace-delimited attributes. The default is "" (an empty string indicating that nothing should be appended).

JSINCLUDES

Comma-separated list of JavaScript (.js) include files for the page. You can use URLs or simple file names. If you use simple file names, the CSP Files Physical Path specifies the physical path to these files. For further information, see the section “Zen Application Configuration.”

PAGENAME

Defines a text string that you can use in titles or labels. If not specified, the Zen page class name is used.

PAGETITLE

Default value for the <page> component’s title attribute.

RESOURCE

Name of a system Resource for which the current user must hold USE privileges in order to view this page or to invoke any of its server-side methods from the client.

RESOURCE may be a comma-delimited list of resource names. In this case, the user must hold USE privileges on at least one of the given Resources in order to use the page.

For further details, see the section “Application Resources” in the “Assets and Resources” chapter of the Caché Security Administration Guide.

SHOWSTATS

If 1 (true) display server statistics (such as the time it took to process the page) within a comment at the end of the page. If 0 (false) do not. The default is 1.

USERPACKAGES

Comma-separated list of user-defined class packages whose HTML class and style definitions are in pre-generated include files.

USERSVGPACKAGES

Comma-separated list of user-defined class packages whose SVG class and style definitions are in pre-generated include files.

Zen Special Variables

Zen offers several special variables to represent instantiated objects of various types within the Zen application. These variables offer a convenient way to reference the methods and properties of these objects from within Zen page or Zen component class code.

Special Variables on the Server Side
Server Side Variable Name Refers To Use in These Classes Use in Zen Runtime Expressions
%application Application object Page No
%composite Current composite object (if any) Page, Component Yes
%page Current page object Page, Component Yes
%query Current ResultSet object (if any) Page, Component Yes
%session Current CSP session object Page, Component Yes
%this Current object (page or component) Page, Component Yes
%url An object whose properties are URI parameters of the current page Page, Component Yes
%zenContext String that indicates what the server code is currently doing Page No

Note that the %url special variable contains values only when a page is first served; If you refer to %url in subsequent method calls or component refreshes, its properties have no value. If you need to refer to a value passed in via a URI parameter, define a property of your page class, link it to a URI parameter using the ZENURL parameter, and then refer to this value via the %page object. For details, see the section “Zen Page URI Parameters.”

Note:

In general, local variable names starting with the % character are for system use only. Local variables that you define in your applications may begin with “%Z” or “%z”; all other percent variables are reserved for system use according to the rules described in “Rules and Guidelines for Identifiers” in the Caché Programming Orientation Guide.

%application

The server-side %application variable is available in every Zen page class. It is intended for use in methods that run while the page object is being created. It refers to the instance, on the server, of the application class that is associated with this page. This allows the page to invoke server-side methods defined in the application class.

For example, the following method is defined in the application class ZENDemo.ApplicationOpens in a new tab in the SAMPLES namespace:

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
}

Page classes that are associated with this application can then invoke this method as follows:

Class ZENDemo.ExpenseCalculator Extends %ZEN.Component.page
{
  /// Application this page belongs to.
  Parameter APPLICATION = "ZENDemo.Application";

        // ...Intervening lines removed...

  /// Return an array of quick links to be displayed by the locator bar.
  ClassMethod GetQuickLinks(Output pLinks) As %Status
  {
    // dispatch to our application class
    Quit %application.GetQuickLinks(.pLinks)
  }
}

There is no client-side equivalent for %application.

%page and zenPage

Zen offers special variables to represent the Zen page object on the client and server sides:

  • In ObjectScript, Caché Basic, or Caché MVBasic code that runs on the server side, the page object is %page:

    Set btn = %page.%GetComponentById("NewBtn1")

  • In Zen runtime expressions, the page object is %page:

    text id="rows" label="Rows:" value="#(%page.Rows)#" size="5"/>

  • In JavaScript methods that run on the client side, the page object is zenPage:

    var chart = zenPage.getComponentById('chart');

  • In JavaScript expressions that are values of XML attributes, the page object is zenPage:

    <button caption="+" onclick="zenPage.makeBigger();"/>

%this, this, and zenThis

Zen offers special variables to represent a Zen object (component or page) on the client and server sides:

  • In ObjectScript, Caché Basic, or Caché MVBasic code that runs on the server side, the object itself is %this:

    Set %this.Name = pSource.Name

  • In Zen runtime expressions, the object itself is %this:

    <html>Item #(%this.tuple)#: <b>#(%query.Title)#</b></html>

  • In JavaScript methods that run on the client side, the object itself is this:

    var out = this.getComponentById('events');

  • In JavaScript expressions that are values of XML attributes, the object itself is zenThis:

    onchange="zenPage.changeLayout('svgFrame',zenThis.getValue());"

%zenContext

When any activity on the server is being initiated by Zen, the server-side special variable %zenContext tells you which type of activity it is. %zenContext is a string. If %zenContext has the value:

  • page, Zen is serving the page in response to an HTTP request

  • submit, Zen is processing a submit request

  • method, Zen is processing a hyperevent

Sometimes it is possible to have trouble with code being executed at compile time. To detect this, check for:

$G(%zenContent)==""

There is no client-side equivalent for %zenContext.

Client Side Functions, Variables, and Objects

This section describes the client-side JavaScript utility functions, variables, and objects that are available for use in Zen page classes.

zen

zen(id)

Find a Zen component by id value. Returns the object that matches the input id.

The zen(id) JavaScript function is equivalent to the following client-side JavaScript method call on the page object:

zenPage.getComponentById(id)

You can use the zen(id) function wherever JavaScript syntax is appropriate; for example:

<button caption="Save" onclick="zen('MyForm').save();"/>

zenEvent

zenEvent.property

Suppose you have:

  • A Zen component such as <listBox>

  • ...with an event handler attribute such as onchange

  • ...whose value is a JavaScript expression

  • ...that invokes a client-side JavaScript method defined in the page class

  • ...such as notifyOnChange in the following <listBox> example:

<listBox id="listBox" label="listBox" listWidth="240px"
         onchange="zenPage.notifyOnChange(zenThis);"
         value="2">
  <option value="1" text="Apple" />
  <option value="2" text="Banana" style="font-size: 1.5em; "/>
  <option value="3" text="Cherry" />
</listBox>

Inside the client-side JavaScript method, such as notifyOnChange, it is convenient to be able to refer to the JavaScript object that represents the event itself (the mouse click or a key press). This object contains supplementary information that you might want to use in processing the event. The zenEvent special variable is available for this purpose. Each zenEvent.type value is a text string; for example, your code might check the event type as follows:

if (zenEvent.type=="mousedown") {
  // do something
  }

The following JavaScript example retrieves the zenEvent.target value to determine the exact target of a mouse click that occurred within a group. The goal is to find out whether the click was on the group itself (which has an enclosing <div> and therefore a zen attribute) or on a component within the group.

// look at source element; IE does not support standard target property.
var target = (null == zenEvent.target) ? zenEvent.srcElement : zenEvent.target;

// all enclosing divs will define an attribute called 'zen'.
var zen = target.getAttribute('zen');
if (zen) {
    // do something
    }

The zenEvent object is a direct analog for the DOM window.event data structure. The following properties are available on the zenEvent object:

  • zenEvent.clientX

  • zenEvent.clientY

  • zenEvent.returnValue

  • zenEvent.srcElement

  • zenEvent.target

  • zenEvent.type may have one of the following values.

    The W3C has defined the type, scope, and handling for these events; however, support for this standard varies from browser to browser. Firefox is generally compliant in its implementation. Internet Explorer implements a commonly used subset of the standard, provides functional alternatives for many parts of the standard that they do not support, and continues to support a wide variety of proprietary legacy events that predate the W3C recommendation. Developers seeking cross-platform support should confine their designs to the events in the following list:

    • beforeunload

    • blur

    • change

    • click

    • contextmenu

    • dblclick

    • error

    • focus

    • keydown

    • keyup

    • keypress

    • load

    • mousedown

    • mouseup

    • mousemove

    • mouseout

    • mouseover

    • reset

    • resize *

    • select *

    • submit

    • unload

    Note:

    For the events marked with an asterisk (*) in the previous list, the name and intent are standard across all browsers, but the scope, target context, and propagation rules are browser specific. Safari implements most of the standard events as well as several platform-specific extensions. Some versions of Safari do not natively recognize dblclick.

zenGetProp

zenGetProp(id,prop)

Returns the value of a named property within a Zen component, where:

  • id is either the id of a component, or the component object itself.

  • prop is the name of the property to set.

The zenGetProp(id,prop) JavaScript function is equivalent to the following ObjectScript method call:

zenPage.getComponentById(id).getProperty(prop)

For example, this JavaScript:

zenGetProp('ctrlButton','hidden');

Is equivalent to this ObjectScript:

zenPage.getComponentById('ctrlButton').getProperty('hidden');

zenIndex

zenIndex(idx)

Find a Zen component by the system-assigned index number of the component. For components within repeating groups, the index may include a dot followed by a 1-based number indicated the position of this component within the repeating group. Returns the object that matches the input idx number.

This JavaScript function is equivalent to the following ObjectScript method call:

zenPage.getComponent( idx)

zenInvokeCallbackMethod

zenInvokeCallbackMethod(attr,ptr,'name',arg1,val1,arg2,val2,arg3,val3)

A utility function used by components to invoke a callback method. The full list of zenInvokeCallbackMethod arguments is as follows. The first three arguments are required; the others are optional:

  • A callback attribute of the component object, for example this.onclick or this.onchange. The value of this attribute must be a JavaScript expression that invokes a client-side JavaScript method.

  • A pointer to the component object, for example this

  • The HTML name for the event (must be quoted)

  • If an optional fourth argument is provided, it is the name of a formal parameter to be passed to the callback method.

  • If there is a fourth argument, a fifth argument must provide a value for the formal parameter.

  • zenInvokeCallbackMethod permits up to two more pairs of argument names and argument values. These are the optional sixth, seventh, eighth, and ninth arguments for zenInvokeCallbackMethod.

For example:

zenInvokeCallbackMethod(this.onclick,this,'onclick')

For more information about this example, see the “Attaching Event Handlers to HTML Elements” section in the chapter “Custom Components.”

zenLaunchPopupWindow

Deprecated. Use a client-side method that calls the Zen page method launchPopupWindow instead. For full information, see “Popup Windows” in the “Popup Windows and Dialogs” chapter of Using Zen Components.

zenPage

zenPage.property

zenPage.method(parameters)

zenPage is the client side equivalent of %page. See “%page and zenPage” in the section “Zen Special Variables.”

zenSetProp

zenSetProp(id,prop,value)

Set the value of a named property within a Zen component, where:

  • id is either the id of a component, or the component object itself.

  • prop is the name of the property to set.

  • value is the value to assign to the property.

The zenSetProp(id,prop,value) JavaScript function is equivalent to the following ObjectScript method call:

zenPage.getComponentById(id).setProperty(prop,value)

For example, this JavaScript:

zenSetProp('ctrlButton','hidden', false);

Is equivalent to this ObjectScript:

zenPage.getComponentById('ctrlButton').setProperty('hidden',false);

zenSynchronousMode

zenSynchronousMode

A flag with the value true (all methods are executed synchronously regardless of whether or not they have return values) or false (methods are synchronous if they have a return value, asynchronous otherwise). See the section “Synchronous and Asynchronous Methods.”

zenText

zenText(id,p1,p2,p3,p4)

zenText is the client side equivalent for $$$Text. See “Localization for Client Side Text” in the chapter “Zen Localization.”

zenThis

zenThis.property

zenThis.method(parameters)

zenThis is the client side equivalent for %this. See “%this, this, and zenThis” in the section “Zen Special Variables.”

Zen Runtime Expressions

Rather than always providing static information in a Zen page description, sometimes it is useful for the page running on the client to be able to invoke runtime expressions that execute on the server. For example, perhaps the caption text for a button needs to be different depending on the identity of the currently logged-in user, which can only be determined from the server at runtime. Various runtime values can play a part in these decisions. For this reason, Zen provides a way for client-side application code to contain expressions that are resolved only on the server, and only at runtime.

Important:

The syntax rules for Zen runtime expressions are extremely specific.

A runtime expression is enclosed within #()# and looks something like this:

<button caption="#(myObject.myProperty)#" />

Where myProperty has a specific value on the server at runtime.

Zen runtime expression syntax can be used, with restrictions, in the following contexts only:

  • XML that is embedded within an XData block (certain XML elements and attributes only)

  • JavaScript that is embedded within a &js<> block in a ZenMethod

  • A JavaScript expression provided as the value of an XML attribute such as onclick

  • HTML that is embedded within an &html<> block in a server-side method

The following items cannot appear within the #()# delimiters of a Zen runtime expression:

  • Methods, such as %this.method(). Only properties are allowed.

  • Properties at a level greater than first level. For example, chaining such as this.parent.other.thing. is not allowed.

  • Calls to macros, such as $$$Text.

  • JavaScript expressions.

  • ObjectScript expressions. The exception to this rule is when a runtime expression appears inside a server-side ObjectScript method or ZenMethod. See “Runtime Expressions in Server-Side Methods.”

Runtime Expressions in XML

Zen runtime expressions can be used within a Zen class XData block, but only the values of specific XML elements and attributes can contain Zen runtime expressions. One such example is the <button> caption attribute:

<button caption="#(%query.Name)#" onclick="alert('#(%query.Name)#')"/>

Of all the possible <button> attributes, only caption, value, and hidden support Zen runtime expressions. The value of onclick in the above example does contain a Zen runtime expression, but this is acceptable because the value of onclick is a JavaScript expression, which may contain #()# syntax. Refer to the restrictions listed in the topic above.

XML elements and attributes that support Zen runtime expressions are clearly identified throughout this book; you may find them by searching for the #()# string in the book text.

Another way to tell that an XML attribute supports runtime expressions is to consult the Caché class documentation to see if the corresponding property has the datatype parameter ZENEXPRESSION set to 1 (true).

You cannot cause a property to support runtime expressions by setting ZENEXPRESSION=1. Built-in Zen component classes provide ZENEXPRESSION=1 to indicate that the property supports runtime expressions, not to enable it to do so.

On the other hand, when you create your own component subclasses, you can set ZENEXPRESSION=1 for properties in those subclasses. It must be a property of a custom component class and not a property of one of the built-in Zen component classes. For details, see the “Datatype Parameters” section in the chapter “Custom Components.”

A significant number of XML attributes support Zen runtime expressions, but only one XML element supports them. This is the <html> component, which may contain literal text and #()# runtime expressions. For example:

<html>Item #(%this.tuple)#: <b>#(%query.Title)#</b></html>

Runtime Expressions and Special Variables

A runtime expression may use the server-side special variables listed in the section “Zen Special Variables.” Zen runtime expressions support the following server-side special variables only:

  • %composite

  • %page

  • %query

  • %session

  • %this

  • %url

By limiting expressions to a predefined set of object property values, the page developer maintains control over which server-side values the client can see. Otherwise it would be possible for a page to execute code that the page developer did not intend, and thus present a security risk.

For example, the %session you see in a Zen expression is not the %session object from the server side. It is a client-side expression that can be evaluated to get a user-data value from the %session object. Suppose your page has a component whose id is test. If your Zen expression uses:

#(%session.test)#

Then on the server this becomes:

%session.Data("test")

And the value associated with the component whose id is test is returned in place of the Zen expression.

For more information about the %session object, see the “%CSP.Session Object” section in the book Using Caché Server Pages (CSP).

Runtime Expressions in Server-Side Methods

The following is an example of a server-side method with an &html<> block, in which a runtime expression invokes the ObjectScript function $ZCONVERT ($ZCVT) to help it to write out the elements of a bulleted list:

Method %DrawHTML()
{
  &html<<div class="demoSidebar">>
  Write $ZCVT(..text,"O","HTML")

  #; bullets
  Set tCount = ..bullets.Count()
  If (tCount > 0) {
    &html<<ul>>
    For n = 1:1:tCount {
      Set tBullet = ..bullets.GetAt(n)
      &html<<li>#($ZCVT(tBullet.text,"O","HTML"))#</li>>
      }
    &html<</ul>>
    }
  }

The following is an example of a ZenMethod with a &js<> block, in which a runtime expression passes the server-side identifier pIndex to JavaScript code that needs to execute on the client. In a later &js<> block, runtime expressions invoke the ObjectScript function $RANDOM ($R) to produce drawing coordinates, and provide CSS style statements to configure a graphical object:

ClassMethod GetSVGContents(pIndex As %Integer) [ ZenMethod ]
{
  #; get the svg component
  &js<var svg = zenPage.getComponent(#(pIndex)#);>

  #; lines
  For i=1:1:30 {
    &js<
      var line = svg.document.createElementNS(SVGNS,'circle');
      //line.setAttribute('x1',200);
      //line.setAttribute('y1',100);
      line.setAttribute('r',5);
      line.setAttribute('cx',#(10+$Random(380))#);
      line.setAttribute('cy',#(10+$Random(180))#);
      line.setAttribute('style',
        '#("fill: yellow; stroke: black; stroke-width: 2;")#');
      svg.svgGroup.appendChild(line);
    >
    }
  Quit
}
Note:

The samples in this topic demonstrate how runtime expressions can be used in a server-side method. In a client-side JavaScript method, you can achieve similar results without using runtime expressions.

Zen Proxy Objects

It can be convenient to have a simple way to pass arbitrary data between the client page and a server process. For this reason, Zen defines a simple generic object called a proxy object. You can:

  1. Create this object within a client page

  2. Pass it to the server (where it is automatically marshalled as a server object)

  3. Use and modify it on the server

  4. Pass values to the client

This section describes this sequence and provides syntax examples.

Proxy Objects on the Client

On the client, a proxy object is simply an instance of a JavaScript zenProxy object. You can create a zenProxy object and fill in its properties as you would any other object; for example:

var proxy = new zenProxy();
proxy.Name = 'John Smith';
proxy.City = 'Boston';

Set Properties

A proxy object is completely generic; it has no predefined properties. You can set whatever property value you wish within a zenProxy object, subject to the following restrictions:

  • The value of a property must be a literal value (string or number) and not an object or function. Only literal values are supported; you cannot set the value of a zenProxy property to be an object. If you need a more complex object like this, build a normal Zen object.

  • The name of a property must be valid in JavaScript.

  • A zenProxy object cannot be a property of a Zen component. If you use the following syntax in a Zen component class, it fails to compile:

    Property a As %ZEN.proxyObject;

    You may subclass %ZEN.Component.objectOpens in a new tab instead.

Clear Properties

You can remove all properties within a zenProxy object by using its clear method:

obj.clear();

Passing a Proxy Object to the Server

You can pass a proxy object to the server by defining a ZenMethod and declaring one of its arguments to be of type %ZEN.proxyObjectOpens in a new tab. For example:

ClassMethod MyMethod(pProxy As %ZEN.proxyObject) As %Boolean [ZenMethod]
{
  Set x = pProxy.Name
  Quit 1
}

Proxy Objects on the Server

When the client invokes MyMethod (as defined in the previous topic) it passes an instance of the zenProxy object to the server. On the server, this instance is automatically converted to a %ZEN.proxyObjectOpens in a new tab with all the same properties as the client object.

The server side %ZEN.proxyObjectOpens in a new tab object is also completely generic, with no predefined properties.

Important:

Do not attempt to use the $Data or $Get function with an instance of %ZEN.proxyObjectOpens in a new tab. If you do so, the function returns an "Invalid Property" error.

Get Properties

You can retrieve values from the %ZEN.proxyObjectOpens in a new tab object using normal property syntax. For example:

 Set a = pProxy.property1
 Set b = pProxy.property2

You can also get the complete set of properties within a %ZEN.proxyObjectOpens in a new tab object as a local array using the %CopyToArray method:

 Do pProxy.%CopyToArray(.array)

If you refer to a property that is not currently defined, you get a value of "" and not an error.

Set Properties

Setting a property automatically defines a property if it is not already defined:

 Set pProxy.property3 = "value"

There is also a %CopyFromArray method to set the complete set of properties within a %ZEN.proxyObjectOpens in a new tab object.

Clear Properties

You can remove all properties from the server side %ZEN.proxyObjectOpens in a new tab object using its %Clear method:

 Do pProxy.%Clear()

Passing Values to the Client

If a ZenMethod that is passed a %ZEN.proxyObjectOpens in a new tab object modifies it, the changes are automatically applied to the client version of the object when the method call completes. This includes any properties that have been added or removed.

Important:

If your ZenMethod has no return value, then it is executed asynchronously. This is Zen normal behavior; for details, see the section “Synchronous and Asynchronous Methods.” In this case, changes to the client-side zenProxy object are still applied, but you do not know when these changes are applied.

It is also possible to define a ZenMethod that returns a %ZEN.proxyObjectOpens in a new tab:

ClassMethod MyCreate() As %ZEN.proxyObject [ZenMethod]
{
  Set tProxy = ##class(%ZEN.proxyObject).%New()
  Set tProxy.Data = "Some data from the server"
  Quit tProxy
}

A client method can call this ZenMethod as follows:

ClientMethod clientMethod() [Language = JavaScript]
{
  var proxy = zenPage.MyCreate();
  alert(proxy.Data);
}

Proxy Object Examples

This section describes built-in Zen components that offer opportunities to use proxy objects.

<html>

The <html> component provides an OnDrawContent callback that can supply the content of the component. This callback is passed a seed value: an application-specific string that is interpreted by the callback method. If the client sets this seed value to a zenProxy object, then it is automatically marshalled as a %ZEN.proxyObject on the server when the OnDrawContent callback is called.

Suppose a Zen page defines an <html> component as follows:

<html id="html" OnDrawContent="DrawHTML"></html>

And also defines OnDrawContent as follows. This OnDrawContent method checks to see if the input argument pSeed is an object, and if so it retrieves and displays its Name and SSN properties. Otherwise, it displays an error message:

ClassMethod DrawHTML(pSeed As %String) As %Status
{
  If $IsObject(pSeed) {
    &html<<table>>
    &html<<tr><td>Name:</td><td>#(pSeed.Name)#</td></tr>>
    &html<<tr><td>SSN:</td><td>#(pSeed.SSN)#</td></tr>>
    &html<</table>>
  }
  Else {
    &html<<i>No data to display</i>>
  }
  Quit $$$OK
}

You could update this <html> component from a client side JavaScript method by instantiating a new zenProxy object, assigning values to Name and SSN properties on that object, assigning this zenProxy object as the value of the seed property for the <html> component, and then refreshing the component, as in the following JavaScript example:


ClientMethod updateHTML() [Language = JavaScript]
{
  // find the html component
  var html = zen('html');
  if (html) {
    var proxy = new zenProxy();
    proxy.Name = 'John';
    proxy.SSN = '222-22-2222';

    // set seed value and invoke server refresh
    html.setProperty('seed',proxy);
    html.refreshContents();
  }
}

For more about the <html> component, see its description in the “Other Zen Components” chapter of Using Zen Components.

<dataController>

The <dataController> component has a method, getDataAsObject(), that returns the current set of properties within a <dataController> as a zenProxy object. This provides an easy way to get data from a controller and pass it to the server for some purpose other than those already built into the <dataController>.

For more about the <dataController> component, see the “Data Controller” section of the “Model View Controller” chapter in Using Zen Components.

<form> and <dynaForm>

The <form> and <dynaForm> components have a method, getValuesAsObject(), that returns the current values of all controls in this form as a zenProxy object. The names of the properties within the proxy object are based on each control’s name attribute.

For more about the <form> and <dynaForm> components, see the “Zen Forms” chapter in Using Zen Components.

Zen JSON Components

The Zen <altJSONProvider> component provides a way to transport object data between a server and client, or between client and server, using JavaScript Object Notation (JSON) format. Zen also provides a specialized version of the <altJSONProvider>, which is discussed in the subsection “altJSONSQLProvider.”

Note:

The Zen component <altJSONProvider> is the replacement for <jsonProvider>, which is no longer specifically described. The <altJSONProvider> component has better performance and can be used in the same way as the <jsonProvider> component. Similarly, <altJSONSQLProvider> is the replacement for <jsonSQLProvider>.

Introduction

JSON refers to a JavaScript programming technique that allows you to define a set of one or more objects using object literal syntax. For example:

var obj = {name:'Bill', home:'New York'};

The Zen <altJSONProvider> component works as follows:

  • Place an instance of the invisible <altJSONProvider> component in your <page> definition in XData Contents.

  • Supply a value for the <altJSONProvider> OnGetTargetObject attribute that is the name of a method in the page class that creates an object or set of objects and returns it. The returned object can be an instance of a specific class or classes, or it can use the Zen proxy object class, %ZEN.proxyObjectOpens in a new tab.

  • Write the OnGetTargetObject callback method. See “The OnGetTargetObject Callback Method.”

  • When the page is rendered, the <altJSONProvider> converts the target object to a set of JavaScript objects. You can see these objects if you view the source of the page as sent to the client.

  • The <altJSONProvider> has a client-side method, getContentObject(), which returns the client-side version of the target object. This is a graph of generic JavaScript Object objects that have the same properties and values as the target objects. If the target object refers to other objects or has collections (literal or object-valued) then the JavaScript object has corresponding object or collection properties. The client can modify these client-side objects or replace them completely using the getContentObject() method.

  • The client can ship its content objects back to the server for processing by calling the submitContent() method. This converts the objects back into server-side objects and invokes the page class method specified by the <altJSONProvider> OnSubmitContent property, if there is one.

  • If you are using OnSubmitContent, write the corresponding method in the page class (see details below). The OnSubmitContent callback method can modify the objects shipped to it or return a completely different set of objects. This makes it possible to use <altJSONProvider> as a way to execute different types of server operations.

  • Alternative actions are available depending on the attributes you choose for the <altJSONProvider> element in XData Contents, and how you write your callback methods in the Zen page class.

Using the <altJSONProvider> component as an object transport has advantages and disadvantages when compared with other mechanisms provided by Zen (such as the built-in transport provided for Zen components). The main advantage is that you can transport data without having to create or modify server classes. You can ship almost any server-side object using this technique. The disadvantages are:

  • You can ship a set of objects, but the objects must form a graph from a parent object down through levels of children; this is due to how JSON format data is reconstituted on the client. You cannot have child objects refer to parents, siblings or other objects outside of the graph.

  • This approach uses late binding, so it is not as efficient as the code-generated approach used by Zen components.

  • Not all object properties are supported: you cannot ship streams or binary values. Only references to child objects are transported.

An <altJSONProvider> element may contain zero or more <parameter> elements. Each <parameter> is passed as an argument to the callback method identified by OnGetTargetObject, OnGetArray, or OnRenderJSON, depending on which is being used to populate the JSON format object. These <altJSONProvider> attributes are mutually exclusive, so only one of them—OnGetTargetObject, OnGetArray, or OnRenderJSON—actually uses the <parameter> values supplied with the <altJSONProvider>.

The <parameter> element has the following attributes.

Attribute Description
paramName The paramName must be unique within the <altJSONProvider>. 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.

The section “The OnGetTargetObject Callback Method.” provides an example using <parameter>. The syntax is the same for the other callbacks. For additional examples of using <parameter> elements with Zen components, see the sections “Data Sources” and “Query Parameters” in the “Zen Tables” chapter of Using Zen Components.

JSON Provider Properties

As an XML element in XData Contents, <altJSONProvider> has the attributes listed in the following table.

Attribute Description
OnGetArray

The name of a callback method defined in the page class. This method is automatically invoked when the page containing this <altJSONProvider> element is rendered. See “The OnGetArray Callback Method.”

OnGetTargetObject

The name of a callback method defined in the page class. This method is automatically invoked when the page containing this <altJSONProvider> element is rendered. See “The OnGetTargetObject Callback Method.”

OnRenderJSON

The name of a callback method defined in the page class. This method is automatically invoked when the page containing this <altJSONProvider> element is rendered. See “The OnRenderJSON Callback Method.”

OnSubmitContent

The name of a callback method defined in the page class. This method is invoked when the client submits an object to the server by calling the client-side method submitContent(). See “The OnSubmitContent Callback Method.”

targetClass

Class name of the target object expected to be served by this <altJSONProvider> component.

You can dynamically set the targetClass value from server-side code by calling the %SetTargetObject(obj) method, where obj is a pointer to a %RegisteredObject.

Several of these attributes override each other, as follows:

  • The OnGetTargetObject callback method provides the view of the object.

  • If an OnGetArray callback is defined, the page invokes the OnGetArray callback to get the view, instead of the OnGetTargetObject callback.

  • If an OnRenderJSON callback is defined, the activities in the OnRenderJSON callback override all of the default behavior of the <altJSONProvider> component when it is rendered. This means the callbacks identified by OnGetTargetObject or OnGetArray are not invoked. The OnRenderJSON callback does all of the work itself.

The OnGetArray Callback Method

The method identified by the OnGetArray property provides an easy way to ship a set of identical objects to the client by filling in a multidimensional array.

The following example shows the required signature for the method identified by OnGetArray:

Method GetArray(ByRef pParameters, 
                Output pMetaData, 
                Output pData) As %Status
{
  Set pMetaData = $LB("name","rank","serialNo")
  Set pData(1) = $LB("Smith","Captain","444-33-2222")
  Set pData(2) = $LB("Jones","Corporal","333-22-3333")
  Quit $$$OK 
}

Where:

  • pParameters is an array of <parameter> values subscripted by paramName . <parameter> values are optional in an <altJSONProvider> definition. You only need to specify them if the callback method requires them. See OnGetTargetObject for a <parameter> example.

  • pMetaData is a $List containing the names of the properties of the objects in the order in which they appear. The method must fill in this structure.

  • pData is an array containing the data. Each node in the array should be a $List containing values for properties. This must match the metadata provided in pMetaData. The array of data can use any subscript value it wants. It is possible to define a hierarchical array. In this case, child nodes are placed within a parent collection called children. The method must fill in this structure.

In the above case the <altJSONProvider> definition would specify:

<altJSONProvider OnGetArray="GetArray" />

The above example would result in two objects being shipped to the client in JSON format as follows:

var content = {
  name:'Smith',rank:'Captain',serialNo:'444-33-2222',
  children:[
    {
    name:'Jones',rank:'Corporal',serialNo:'333-22-3333'
    }
  ]
};

The OnGetTargetObject Callback Method

The following example shows the required signature for the method identified by OnGetTargetObject:

Method GetTarget(ByRef pParameters, 
       Output pObject As %RegisteredObject) As %Status
{
  Set pObject = ##class(MyApp.MyClass).%New()
  Set pObject.Name = "Bob"
  Set pObject.Department = pParameters("Dept")
  Quit $$$OK 
 }

Where:

  • pParameters is an array of <parameter> values subscripted by paramName . <parameter> values are optional in an <altJSONProvider> definition. You only need to specify them if the callback method requires them.

  • pObject is an instance of the object whose data is to be provided to the client in JSON format. The method must return this object by reference. pObject can be any object that satisfies JSON restrictions as described in this topic. In fact, pObject can be a Zen proxy object as described in the “Zen Proxy Objects” section.

Important:

InterSystems recommends that you do not ship a persistent object direct from the database in this fashion, especially if it has references to other objects, as you may end up shipping your entire database to the client. You should always use a simpler wrapper object in this case.

In the above case the <altJSONProvider> definition would specify:

<altJSONProvider OnGetTargetObject="GetTarget" >
  <parameter paramName="Dept" value="Sales" />
</altJSONProvider>

The OnRenderJSON Callback Method

Note:

This is an advanced callback for very special situations. It lets you write out the exact JSON content you wish to render to the client. You should be an expert in JavaScript debugging to use this feature.

The following example shows the required signature for the method identified by OnRenderJSON:

Method RenderHandler(ByRef pParameters) As %Status
{
  /// Render activities here
  Quit tSC
}

Where:

  • pParameters is an array of <parameter> values subscripted by paramName . <parameter> values are optional in an <altJSONProvider> definition. You only need to specify them if the callback method requires them. See OnGetTargetObject for a <parameter> example.

In the above case the <altJSONProvider> definition would specify:

<altJSONProvider OnRenderJSON="RenderHandler" />

The OnSubmitContent Callback Method

The following example shows the required signature for the method identified by OnSubmitContent:

Method SubmitHandler(pCommand As %String, 
       pProvider As %ZEN.Auxiliary.altJSONProvider,
       pObject As %RegisteredObject,
       Output pResponse As %RegisteredObject) As %Status
{
  Set tSC = $$$OK
  If ($IsObject(pObject)) {
    Set tSC = pObject.%Save()
  }
  Quit tSC
}

Where:

  • pCommand is the command string supplied to submitContent().

  • pProvider is the JSON provider object.

  • pObject is the submitted object after it has been converted from JSON format back into an object instance.

  • If the callback method returns an object via the pResponse argument, this object is returned to the client and becomes the new content of the JSON provider.

In this case the <altJSONProvider> definition would specify:

<altJSONProvider OnSubmitContent="SubmitHandler" />

JSON Provider Methods

A <altJSONProvider> element is the XML projection of the %ZEN.Auxiliary.altJSONProviderOpens in a new tab class. Objects of this class offer several methods that you can call to work with the component on the client side. The following table describes them.

Client Side Method Description
getContentObject() Return the client-side JSON data as an object, or if not available return null.
getError() Get the current value of the error encountered on the server side. For details, see submitContent().
reloadContents()

Reload the contents of the JSON provider with data from the server. Unlike the submitContent() method, reloadContents() does not send data to the server.

reloadContents() is typically used in conjunction with the OnGetArray callback: reloadContents() calls the server and the server, in turn, calls the OnGetArray callback to create new content to ship back to the client.

setContentObject(obj) Make obj the new target object for this JSON provider.
setContentText(json) Set the content for this provider using the string provided in the json argument. json is expected to contain object data in JSON format.
submitContent(cmd,target)

Send the current target object for this provider to the server for processing. This recreates the object on the server and invoke the OnSubmitContent callback method.

submitContent() has the following arguments:

  • cmd is an optional string passed to the server callback method to allow for different behaviors in the server logic.

  • target is optional. If specified, it gives the name of the server-side class that you wish to have instantiated on the server. This has the same effect as setting the targetClass property of the JSON provider. The target argument makes it possible to submit content for different object classes. If the server cannot create an instance of the specified class, it records an error.

The submitContent() method returns true if successful and false otherwise. If the method fails on the server, it records a string describing the error. This string can be retrieved on the client side using the getError() method.

altJSONSQLProvider

Note:

The Zen <altJSONSQLProvider> component is the replacement for the <jsonSQLProvider> component, which is no longer specifically described. The <altJSONSQLProvider> component has better performance and can be used in the same way as the <jsonSQLProvider> component.

The <altJSONSQLProvider> component uses an SQL statement to supply data to the client. You can provide the SQL statement in the following ways:

  • As the value of the sql attribute

  • In the OnGetSQL callback method

  • As a class query by providing queryClass and queryName attributes

The following code fragment illustrates use of the sql attribute.

<page xmlns="http://www.intersystems.com/zen">
  <altJSONSQLProvider id="json"
    sql="SELECT ID,Name,SSN,DOB FROM Sample.Person" />
  <dataGrid id="grid" controllerId="json"/>
</page>

You can also supply parameters to the SQL statement, as illustrated in this example:

<page xmlns="http://www.intersystems.com/zen">
  <altJSONSQLProvider id="jsonSQL" 
    sql="SELECT ID,Name,Age FROM Sample.Person where Name %STARTSWITH ? or Age ?"  >
    <parameter paramName="1" value="Z"/>
    <parameter paramName="2" value="80"/>
  </altJSONSQLProvider>
  <dataGrid id="grid" controllerId="jsonSQL"/>
</page>

The next code fragment sets the OnGetSQL attribute of the <altJSONSQLProvider>, and provides parameters that are passed to the event handler.

<page xmlns="http://www.intersystems.com/zen">
  <altJSONSQLProvider id="jsonSQL" OnGetSQL="GetSQL" >
    <parameter paramName="1" value="Z"/>
    <parameter paramName="2" value="80"/>
  </altJSONSQLProvider>
  <dataGrid id="grid" controllerId="jsonSQL"/>
</page>

This code sample shows an implementation of the OnGetSQL callback method, which uses the parameters supplied in the <altJSONSQLProvider>.


Method GetSQL(ByRef pParm As %String,
 ByRef pSQL As %String,
 pCriteria As %ZEN.proxyObject,
 ByRef pPagingInfo As %String) As %Status
{
    Set pSQL = "SELECT ID,Name,Age FROM Sample.Person where Name %STARTSWITH ? or Age > ?"
    Quit $$$OK
}

The next example uses a class query to provide the SQL statement:

<page xmlns="http://www.intersystems.com/zen">
  <altJSONSQLProvider id="jsonSQL" 
    queryClass="Sample.Person" queryName="ByName" >
    <parameter paramName="1" value="Z"/>
  </altJSONSQLProvider>
  <dataGrid id="grid" controllerId="jsonSQL"/>
</page>

See the section “Referencing a Class Query” in the book Using Zen Components for more information on using a class query to supply an SQL statement.

The <altJSONSQLProvider> component defines the following attributes:

Attribute Description
arrayName

The name of the output array. The default value is "children".

currPage

If the provider is using server-side data paging, this is the 1-based number of the current page.

OnGetSQL

This specifies a callback method that returns an SQL query (string) that drives this provider. This is identical in behavior to (and replaces) the sqlOpens in a new tab property. The method can make it easier to create queries based on parameter values or search criteria passed via the criteriaOpens in a new tab property.

pageSize

If the provider is using server-side data paging, this is the number of records in each page.

recordCount

If the provider is using server-side data paging, this is the total number of records.

The methods %WriteJSONFromSQL and %WriteJSONStreamFromSQL also support use of class queries and parameters. The first example shows use of a class query with %WriteJSONFromSQL.

    set pid="Z" 
    set provider=##class(%ZEN.Auxiliary.altJSONSQLProvider).%New() 
    set provider.queryClass="Sample.Person" 
    set provider.queryName="ByName" 
    set param=##class(%ZEN.Auxiliary.parameter).%New() 
    set param.value=pid 
    do provider.parameters.SetAt(param,1) 
    do provider.%WriteJSONFromSQL("","",,1000,,provider) 
    quit

The next example provides two parameters to the SQL statement supplied to %WriteJSONFromSQL.

    set pid="Z" 
    set provider=##class(%ZEN.Auxiliary.altJSONSQLProvider).%New() 
    set query="Select * from Sample.Person where Name %STARTSWITH ? or Age > ?" 
    set provider.sql=query 
    set param=##class(%ZEN.Auxiliary.parameter).%New() 
    set param.value=pid 
    set param1=##class(%ZEN.Auxiliary.parameter).%New() 
    set param1.value=20 
    do provider.parameters.SetAt(param,1) 
    do provider.parameters.SetAt(param1,2) 
    do provider.%WriteJSONFromSQL("","",,1000,,provider) 
    quit

The final example provides two parameters to the SQL statement supplied to %WriteJSONStreamFromSQL.

    set pid="Z" 
    set provider=##class(%ZEN.Auxiliary.altJSONSQLProvider).%New() 
    set query="Select * from Sample.Person where Name %STARTSWITH ? or Age > ?" 
    set provider.sql=query 
    set param=##class(%ZEN.Auxiliary.parameter).%New() 
    set param.value=pid 
    set param1=##class(%ZEN.Auxiliary.parameter).%New() 
    set param1.value=30 
    do provider.parameters.SetAt(param,1) 
    do provider.parameters.SetAt(param1,2) 
    do provider.%WriteJSONStreamFromSQL(.stream,"",,1000,,provider) 
    quit    

Zen Page Event Handling

Many components define event handlers such as onclick, onchange, etc. See the description of any Zen control component, such as <button>, <image>, or <submit>, in the “Zen Controls” chapter of Using Zen Components. Also see the description of zenEvent in the section “Client Side Functions, Variables, and Objects.”

While a page is being displayed, Zen traps all events until the page finished building. This is to prevent the user from clicking on an element before the browser has finished building the zenPage object on the client side.

Zen Page URI Parameters

Sometimes it is useful to pass values to a Zen page via URI parameters. The ZENURL datatype parameter enables this feature.

Suppose that when you define a property in a Zen page class you apply the datatype parameter ZENURL to it, as follows:

Property employeeID As %ZEN.Datatype.string(ZENURL="ID");

The previous example assigns a URI parameter with the name ID to the class property with the name employeeID. When this Zen page is requested by passing a URI to a browser, the value specified for ID is assigned to employeeID. The following URI:

MyApp.MyPage.cls?ID=48

Causes the following code to run:

 Set %page.employeeID = $GET(%request.Data("ID",1))

If the URI parameter assigned to a property does not pass the property’s validation test for any reason, such as a value greater than the property’s MAXVAL, the page is not displayed and an error message is displayed instead.

Zen Layout Handlers

The actual work of laying out a group component’s children is handled by the %ZEN.LayoutManagerOpens in a new tab class. It is possible to implement layout strategies in addition to those normally provided. If you wish to serve the page using your own, custom layout handler code, you can.

Zen provides a simple, built-in layout strategy with the intention of making rapid application development easier. Components start at top left of the page and work their way to the right and bottom. You can achieve a lot, very quickly, by manipulating nests of vertical and horizontal groups.

There may be cases when your layout needs finer control. If so, ultimately it might suit your project best to develop your own layout handler for use with Zen. For these cases, Zen provides an onlayoutHandler callback method. This client-side method, if defined, is called when a page is first displayed and whenever the size of the page is changed thereafter. This provides a way to define code that adjusts the size and position of components whenever the size of the containing page is changed.

For example:


ClientMethod onlayoutHandler(load) [ Language = javascript ]
{
   // adjust size of lookout menu
   var menu = zen('lookout');

   // find div for titleBox & mainMenu
   var title = zen('titleBox');
   var divTitle = title.getEnclosingDiv();

   // find height of window
   var winHeight = zenGetWindowHeight();

   // adjust size of menu
   var sz = winHeight - (parseInt(divTitle.offsetHeight)) - 20;
   menu.setSize(null,sz);
}

Zen Utility Methods

The %ZEN.UtilsOpens in a new tab class includes a number of utility methods for performing various common functions. Most of these classes are for internal use by the Zen library.

ZENTest.LogPageOpens in a new tab in the SAMPLES namespace is an example of a page class that uses some of these utility methods to respond to the user’s selection of whether or not to enable logging for the application. The page class provides:

  • A <tablePane> definition for the event log display, plus the following <checkbox>:

    <checkbox id="cbEnabled" caption="Logging Enabled"
              onchange="zenPage.enabledChange(zenThis);"/>
    
  • This client-side method:

    
    ClientMethod enabledChange(cb) [ Language = javascript ]
    {
      this.EnableLog(cb.getValue()==1 ? true : false);
      this.refreshLog();
    }
    
  • And this Zen method:

    ClassMethod EnableLog(flag As %Boolean = "") As %Boolean [ ZenMethod ]
    {
      If (flag = "") {
        Set flag = ##class(%ZEN.Utils).%LoggingEnabled()
      }
      If (flag) {
        Do ##class(%ZEN.Utils).%StartLog()
      }
      Else {
        Do ##class(%ZEN.Utils).%StopLog()
      }
      Quit 1
    }

The result is as follows:

Sample Log of Zen Events
generated description: event log

To read more about %ZEN.UtilsOpens in a new tab, start the InterSystems online documentation system and choose Class Reference Information for the %SYS namespace.

FeedbackOpens in a new tab