Sie sind auf Seite 1von 54

Creating GUI Classes in TIBCO General Interface 1

Creating GUI Classes in TIBCO General


Interface

This document describes and presents examples on how to create GUI classes in
TIBCO General Interface.

Topics

• About GUI Classes, page 2


• Exercise 1: Basic Spinner, page 4
• Exercise 2: Spinner with Up and Down Buttons, page 11
• Exercise 3: Spinner with Up and Down Image Buttons, page 14
• Exercise 4: Editable Grid, page 16
• Appendix A, System Resolvers, page 27
• Appendix B, Control Statements, page 32
• Appendix C, Contextual Variables, page 53

TIBCO General Interface GUI Classes


2 Creating GUI Classes in TIBCO General Interface
|

About GUI Classes

Although TIBCO General Interface has dozens of pre-built GUI classes ranging
from sliders to tree-grids, developers may need to build their own controls. In the
past this has been difficult, due to the complex APIs that General Interface uses to
manage browser layout differences. With the inclusion of the new class,
j s x 3 . g u i . T e m p l a t e , General Interface now provides a base class for you to use
when creating your own user controls. A new schema also allows you to
declaratively create layouts using familiar HTML syntax.

Background
TIBCO General Interface employs a model-view-controller (MVC) architecture.
For each on-screen object (the “view”) there is a corresponding state model that
describes it (the “model”). Logical functions synchronize the two (the
“controller”). For example, consider a slider widget with the model shown in
Table 1.

Table 1 Model for Slider Widget

Property Name Value


length 200

value 25

orientation horizontal

name myslider

When it is time to paint the model, the corresponding view is generated as HTML.

Note: Some content in the following code has been removed for readability.

<span onmousewheel=";" onkeydown=";"


style="position:absolute;width:200px;height:15px;">
<div style="position:relative;width:200px;height:7px;padding:4px
0px 4px 0px;;">
<div onclick=";" style="
width:198px;height:5px;background:gray;border:inset 1px;"/>
<div onmousedown=";"
style="position:absolute;width:15px;height:15px;left:50px;top:0px;
">

TIBCO General Interface GUI Classes


About GUI Classes 3
|
<div style=" width:15px;height:15px;
background:url(JSX/images/slider/top.gif);"/>
</div>
</div>
</span>

The HTML is then sent to the browser (the view delegate—basically a helper that
can render the HTML) and it renders as follows:

If the user interacts with the view by dragging the slider handle, the controller is
notified of the relevant events, including mousedown, mousemove, and
mouseup. The controller then updates the model based upon its own processing
rules. In this case, assume the slider has been dragged to position 50 (Table 2).

Table 2 Example Position

Property Name Value


value 50

The system then synchronizes the model to the view (which is sent to the browser
to reflect the updated state), and the cycle continues.

Like all architectures, MVC is merely an organizing schema—a way to describe


objects and their relationships to help better understand and build complex
systems. By abstracting the view and the model, you are able to envision your
Web application not as a tangle of HTML, but rather a group of higher-level
objects. Being freed to imagine the model, you can more easily architect
appropriate business solutions.
The remainder of this document describes how to create custom GUI classes
according to MVC principles. The exercises become progressively more complex
as the various capabilities of the template language are described. All of the
examples describe how to create a basic spinner control.

TIBCO General Interface GUI Classes


4 Creating GUI Classes in TIBCO General Interface
|

Exercise 1: Basic Spinner

This example describes how to create a basic spinner control.

I. Create a Functional Specification


It is important to create a solid functional specification. With a good specification,
it is not necessary to waste time modifying and re-factoring code. Requirements
for the basic spinner include an HTML text input control with enhanced features
and functionality to allow for key input and listening for keydown and
mousewheel events. When the up arrow key is pressed, the control will increment
its value. The mouse wheel will have a similar effect when wheeled up by the
user. By default, the control will have a single-pixel gray border. Table 2 shows the
complete list of controls, and Table 3 shows the associated events and behavior.

Table 3 Example 1 Controls

Property Name Value


position relative

left

top

width 60 (pixels)

height 20 (pixels)

border solid 1px gray

vertical-align middle

font-name

font-size 20

value 1

TIBCO General Interface GUI Classes


Exercise 1: Basic Spinner 5
|

Table 4 Example 1 Events and Behavior

Property Name Value


Key Up Increments the text field

Key Down Decrements the text


field

Mousewheel Up Increments the text field

Mousewheel Down Decrements the text


field

Change Persists the value after


focus is lost

value 1

The default control will appear as follows:

II. Define the class


Authoring a GUI class requires a working knowledge of HTML, XML, and
JavaScript. However, given the functional separation that MVC provides, it is
possible to divide the tasks among team members who are appropriately skilled
for each portion.
The following steps show how to create the GUI class.
1. Create a new JavaScript file.
a. Require the template engine and any interfaces. The new spinner control
will eventually be used as a form field in a General Interface application,
so the interface, j s x 3 . g u i . F o r m , will also be included in the require
statement. Refer to the documentation for j s x 3 . g u i . F o r m for a complete
list of all methods provided by the interface.
b. Define the new class using d e f i n e C l a s s . Refer to the super class
(j s x 3 . g u i . T e m p l a t e . B l o c k ), and any interfaces (j s x 3 . g u i . F o r m ). While
S P I N N E R is a pointer to the class, s p i n n e r is a pointer to the class
prototype. These names are arbitrary and merely serve as a useful alias

TIBCO General Interface GUI Classes


6 Creating GUI Classes in TIBCO General Interface
|

while defining the class. Also note the name for the new class:
my.Spinner1.

c. Include an i n i t method. This is the constructor that allows the class to be


instantiated. It is object oriented and should therefore make a call to
j s x s u p e r. Users can instance the class by calling: new
my.Spinner1("somename");

d. Subclass the g e t T e m p l a t e X M L method to convert the template's HTML


markup (to be created) into the corresponding JavaScript functions needed
by the class. The XML is defined inline within the JavaScript and uses an
apostrophe escape.
jsx3.require("jsx3.gui.Template","jsx3.gui.Form"); //a)

jsx3.lang.Class.defineClass("my.Spinner1" //b)
,jsx3.gui.Template.Block
,[jsx3.gui.Form]
,function(SPINNER,spinner) {

spinner.init = function(strName) { //c


this.jsxsuper(strName);
};

spinner.getTemplateXML = function() {//d


return ['<transform ',
'xmlns="http://gi.tibco.com/transform/" ',
'xmlns:u="http://gi.tibco.com/transform/user"
version="1.0">',
' <model/>' ,
' <template/>' ,
'</transform>'].join(""); };

});

2. Define any model defaults for the GUI control. The spinner will implement a
default value of 1 if no value is specified. Because the control implements the
j s x 3 . g u i . F o r m interface, its value is stored using the j s x v a l u e field. This
means that any call to the g e t V a l u e method (a mix-in method provided by
j s x 3 . g u i . F o r m ) will return 1 if the given instance does not define a value.

jsx3.require("jsx3.gui.Template","jsx3.gui.Form");

jsx3.lang.Class.defineClass("my.Spinner1"
,jsx3.gui.Template.Block
,[jsx3.gui.Form]
,function(SPINNER,spinner) {

spinner.init = function(strName) {
this.jsxsuper(strName);
};

TIBCO General Interface GUI Classes


Exercise 1: Basic Spinner 7
|
spinner.jsxvalue = 1;

spinner.getTemplateXML = function() {
return ['<transform ',
'xmlns="http://gi.tibco.com/transform/" ',
'xmlns:u="http://gi.tibco.com/transform/user"
version="1.0">',
' <model/>' ,
' <template/>' ,
'</transform>'].join("");
};

});

The system serializes all simple model properties. This means that serializing
an instance by calling its t o X M L method will result in the property value, 1,
being serialized, regardless of whether or not it was explicitly declared on the
instance.
3. Author the HTML prototype to reflect the requirements in the functional
specification:
<input type="text" value="1"
onchange=";" onkeydown=";" onkeyup=";" onmousewheel=";"
style="position:relative;left:;top:;width:60px;height:20px;
margin:0px;border:solid 1px gray;font-name:;
font-size:10px;vertical-align:middle;" />

Next, replace every HTML attribute, event, or style property value that should
be made dynamic with a replacement variable (denoted by curly braces). For
example, to allow the font-size property to be dynamically replaced with the
model property, j s x f o n t s i z e , change the explicit style declaration from this:
font-size:10px;

to this:
font-size:{jsxfontsize};

The HTML now appears as follows.


<input type="text" value="{jsxvalue}"
onchange="{onchange}" onkeydown="{onkeydown}"
onkeyup="{onkeyup}" onmousewheel="{onmousewheel}"
style="position:{$position};left:{jsxleft};top:{jsxtop};

width:{jsxwidth|60};height:{jsxheight|20};margin:{jsxmargin}
;
border:{border|solid 1px gray};font-name:{jsxfontname};
font-size:{jsxfontsize};vertical-align:middle;" />

TIBCO General Interface GUI Classes


8 Creating GUI Classes in TIBCO General Interface
|

Note the following items:


• The position property uses the variable { $ p o s i t i o n } . The $ prefix is reserved
by the system to define a pre-built set of system variable resolvers that are
provided by the template engine. Appendix A on page 27 contains a full list of
pre-built resolvers. By using these you can greatly simplify creating your
template. For example, the { $ p o s i t i o n } resolver will return either r e l a t i v e
or a b s o l u t e , depending upon the value of the jsxrelativeposition property.
• The width, height, and border declarations include a default value. This
ensures that the control is at least 60 pixels wide by 20 pixels tall with a
single-pixel gray border. Default values are denoted using a pipe character.
Refer to Appendix B on page 32 for implementation details on
@defaultvalue.

• Event declarations such as o n k e y d o w n = " { o n k e y d o w n } " tell the system that


you have a controller method named o n k e y d o w n that should be notified of the
keydown event. Step 4 explains this further.
• The value field for the text input uses the { j s x v a l u e } variable. This means
that when the control is painted on-screen its value will be equal to
t h i s . j s x v a l u e . This is how all template variables work and is the preferred
method for matching properties on the model to properties in the view
template. In other words, { j s x f o n t s i z e } points to t h i s . j s x f o n t s i z e and
{ x . y . z } points to t h i s . x . y . z . This simple pattern ensures proper
synchronization between model and view.
The class appears as follows:
jsx3.require("jsx3.gui.Template","jsx3.gui.Form");

jsx3.lang.Class.defineClass("my.Spinner1"
,jsx3.gui.Template.Block
,[jsx3.gui.Form]
,function(SPINNER,spinner) {

spinner.init = function(strName) {
this.jsxsuper(strName);
};

spinner.jsxvalue = 1;

spinner.getTemplateXML = function() {
return ['<transform
xmlns="http://gi.tibco.com/transform/" ' ,
' xmlns:u="http://gi.tibco.com/transform/user"
version="1.0">' ,
' <model/>' ,
' <template>' ,
' <input type="text" value="{jsxvalue}" ' ,
'onchange="{onchange}" onkeydown="{onkeydown}" ' ,
'onkeyup="{onkeyup}" onmousewheel="{onmousewheel}" ' ,

TIBCO General Interface GUI Classes


Exercise 1: Basic Spinner 9
|
'
style="position:{$position};left:{jsxleft};top:{jsxtop};' ,
'
width:{jsxwidth|60};height:{jsxheight|20};margin:{jsxmargin}
;' ,
'border:{border|solid 1px
gray};font-name:{jsxfontname};',
'font-size:{jsxfontsize};vertical-align:middle;"
/>' ,
' </template>' ,
'</transform>'].join("");
};

});

4. The last step is to define the controller functions. As previously explained, any
event declared in the template using the standard variable syntax (such as
o n k e y d o w n = " { o n k e y d o w n } " ) will automatically call the prototype class
function of the same name. This means if you declare an o n k e y d o w n event in
your template, you should also declare the JavaScript controller o n k e y d o w n .
By naming the controllers appropriately, the on-screen view will be able to
locate the corresponding in-memory controller. When found, an instance of
j s x 3 . g u i . E v e n t and a reference to the target GUI object will be passed to the
controller. The controller can then update the model and the view by calling
the s e t P r o p e r t y method. This single call updates the named property on the
model and projects the update to the on-screen view. Note how the s e t V a l u e
method utilizes this function call. This is a final critical step that all controller
methods should use to ensure a complete feedback loop from model to view.
The final class is as follows (the i n i t and c o m p i l e methods have been
removed for readability):
jsx3.require("jsx3.gui.Template","jsx3.gui.Form");

jsx3.lang.Class.defineClass("my.Spinner1"
,jsx3.gui.Template.Block
,[jsx3.gui.Form]
,function(SPINNER,spinner) {

// ** init and compile methods removed for readability **

//add the controller logic here...


spinner.onkeydown = function(objEvent,objGUI) {
if(objEvent.upArrow()) this.up();
else if(objEvent.downArrow()) this.down();
};

spinner.onkeyup = function(objEvent,objGUI) {
this.jsxvalue = objGUI.value;
};

spinner.onchange = function(objEvent,objGUI) {
this.jsxvalue = objGUI.value;
};

TIBCO General Interface GUI Classes


10 Creating GUI Classes in TIBCO General Interface
|

spinner.onmousewheel = function(objEvent,objGUI) {
var wd = objEvent.getWheelDelta();
if (wd > 0) this.up();
else if(wd < 0) this.down();
};

spinner.up = function() {
this.setValue(parseInt(this.getValue())+1);
};

spinner.down = function() {
this.setValue(parseInt(this.getValue())-1);
};

spinner.setValue = function(strValue) {
//tell the engine to update the model and synchronize the
view
this.setProperty("jsxvalue",strValue);
};

});

TIBCO General Interface GUI Classes


Exercise 2: Spinner with Up and Down Buttons 11
|

Exercise 2: Spinner with Up and Down Buttons

This example describes how to create a spinner control that includes up and down
buttons.

I. Define the control


This exercise adds up and down image buttons to increment or decrement the
value of the input box. The control otherwise remains unchanged.

II. Define the class


All controllers remain the same for this class. This is an important point that
reflects the flexibility provided by an MVC architecture. Because this exercise
merely adds features to the view, there is no need to modify any of the existing
controller methods. Only the view template and a few additional model defaults
need to be changed, because the updated spinner widget must define more
HTML elements to create the up and down images.
The following steps show how to add the up and down controls.
1. Add two additional model defaults. The new spinner control will use up and
down images borrowed from the j s x 3 . g u i . T i m e P i c k e r class. The URL for
the up and down images is saved as a complex object (t h i s . a r r o w . u p and
t h i s . a r r o w . d o w n ). The General Interface serializer ignores this property
when the object is serialized as XML, because there is no need to save it, as it is
only used when painting the arrows at runtime. The class appears as follows:
jsx3.require("jsx3.gui.Template","jsx3.gui.Form");

jsx3.lang.Class.defineClass("my.Spinner1"
,jsx3.gui.Template.Block
,[jsx3.gui.Form]
,function(SPINNER,spinner) {

spinner.init = function(strName) {
this.jsxsuper(strName);
};

spinner.jsxvalue = 1;
this.arrow = {};
this.arrow.up = "url(" +
jsx3.resolveURI("jsx:///images/jsxtimepicker/" +
"spin_up.gif") + ")";

TIBCO General Interface GUI Classes


12 Creating GUI Classes in TIBCO General Interface
|
this.arrow.down = "url(" +
jsx3.resolveURI("jsx:///images/jsxtimepicker/" +
"spin_down.gif") + ")";

spinner.getTemplateXML = function() {
return ['<transform
xmlns="http://gi.tibco.com/transform/" ' ,
' xmlns:u="http://gi.tibco.com/transform/user"
version="1.0">' ,
' <model/>' ,
' <template/>' ,
'</transform>'].join("");
};

});

2. Expand the definition for the HTML template. Note that the input box is
nested inside a special box, i n l i n e b o x . This is an adapted SPAN object
provided by the template engine that allows the GUI control to have layout
(width and height) even if it is relatively positioned or flowed inline with text.
This enables you to position the control either relatively or absolutely without
having cross browser differences corrupt the layout.
<inlinebox style="position:{$position};left:{jsxleft};
top:{jsxtop};width:{jsxwidth|60};height:{jsxheight|20};
margin:{jsxmargin};border:{jsxborder|solid 1px gray};
vertical-align:middle;">
<input type="text" value="{jsxvalue}" onchange="{onchange}"
onkeydown="{onkeydown}" onkeyup="{onkeyup}"
onmousewheel="{onmousewheel}"
style="position:absolute;left:0px;top:0px;
width:100%-12px;height:100%;
border:{iborder|0px;solid 1px gray;0px;0px;};
font-name:{jsxfontname};font-size:{jsxfontsize};" />
<span onclick="{up}"
style="position:absolute;right:11px;top:0px;
width:11px;height:8px;overflow:hidden;cursor:pointer;
background-image:{arrow.up};"></span>
<span onclick="{down}"
style="position:absolute;right:11px;top:8px;
width:11px;height:8px;overflow:hidden;cursor:pointer;
background-image:{arrow.down};"></span>
</inlinebox>

TIBCO General Interface GUI Classes


Exercise 2: Spinner with Up and Down Buttons 13
|

Note the following items:


• Use simple dot notation when referring to complex fields. For example, to
reference the instance/prototype field, arrow.up, use a variable named
{arrow.up}. In general any field can be referenced this way.
• The up and down events were defined in the previous exercise when the
controller methods were declared. These methods already existed and were
used by other methods, such as o n k e y d o w n and o n m o u s e w h e e l . Simply
referring to these by name (for example, o n c l i c k = " { u p } " ) makes them
callable when a click event occurs in the view.

TIBCO General Interface GUI Classes


14 Creating GUI Classes in TIBCO General Interface
|

Exercise 3: Spinner with Up and Down Image Buttons

This example describes how to create the same spinner control as in Exercise 2 on
page 11 using the a t t a c h tag.

I. Define the control


The use case for this control is exactly the same as for Exercise 2; however, in this
exercise the up and down image buttons are generated using the a t t a c h tag.

II. Define the class


The template for this class requires fewer HTML tags than the previous example
to render its up and down buttons. Instead, it uses the a t t a c h tag to paint the
HTML for a specific set of descendant objects. (Refer to Appendix B on page 32
for a list of all statements that you can use in your template.)
Figure 1 shows the Component Hierarchy palette in GI Builder and how the
spinner uses the I m a g e B u t t o n children to render its arrows.

Figure 1 Component Hierarchy with Spinner Controls


7

The following template can render the above DOM structure.


<inlinebox style="position:{$position};left:{jsxleft};
top:{jsxtop};width:{jsxwidth|60};height:{jsxheight|20};
margin:{jsxmargin};border:{jsxborder|solid 1px gray};
vertical-align:middle;">
<input type="text" value="{jsxvalue}" onchange="{onchange}"
onkeydown="{onkeydown}" onkeyup="{onkeyup}"
onmousewheel="{onmousewheel}"

TIBCO General Interface GUI Classes


Exercise 3: Spinner with Up and Down Image Buttons 15
|
style="position:absolute;left:0px;top:0px;
width:100%-12px;height:100%;
border:{iborder|0px;solid 1px gray;0px;0px;};
font-name:{jsxfontname};font-size:{jsxfontsize};" />
<attach select="new
jsx3.util.List(this.getChildren()).iterator()">
<drawspace boxtype="'box'" position="'absolute'"
top="$$target.getChildIndex() * 8"
left="$$parentwidth - 11" width="11" height="8"/>
</attach>
</inlinebox>

Every attach tag requires a s e l e c t statement that returns an instance of


j s x 3 . u t i l . I t e r a t o r. This provides flexibility when deciding which descendant
objects should paint. In the above example the s e l e c t statement returns all child
objects (these are the two child I m a g e B u t t o n s shown in the DOM). No error
checking is in place; however, you can implement error checking to ensure that
the control only paints children of type j s x 3 . g u i . I m a g e B u t t o n .
When attaching descendants, you can use a d r a w s p a c e tag to send additional
sizing and processing directives to the children. This provides a way for a parent
control to force the position and content of any painted descendants. Although
this is not always necessary, it is a useful convenience. All fields on the drawspace
tag are calculated. This means that strings should be escaped where appropriate:
boxtype="'box'"

Because all fields are calculated, any valid JavaScript can be used. For example,
the top value is calculated based upon the child index of the target child.
top="$$target.getChildIndex() * 8"

The $ $ t a r g e t variable is always available within a t t a c h and f o r - e a c h tags and


points to the current object in the iteration. In this case, the child at index 0 will
render at position 0, the child at position 1 will render at position 8. Appendix C
on page 53 lists all contextual system variables (those beginning with $ $ ). You can
embed these variables within your own code and use them for your own
calculations. For example, the variable $ $ p a r e n t w i d t h defines the width of the
drawspace into which the given HTML tag will paint. In the above markup, it
determines where to draw the left position of the I m a g e B u t t o n children:
left="$$parentwidth - 11"

TIBCO General Interface GUI Classes


16 Creating GUI Classes in TIBCO General Interface
|

Exercise 4: Editable Grid

This exercise describes how to create an editable grid (similar to a spreadsheet)


that allows variable number of rows and columns.

I. Define the control


The data source for the grid is an XML document in Common Data Format (CDF).
Each record in the CDF document has an associated on-screen row in the grid.
The columns are defined using JSON, which allows you to specify for each
column the column label, column width, and CDF attribute to which the column
will map.
When users click a cell in the grid, a text input overlays the cell, allowing the grid
to be editable. Both before-edit and after-edit model events are exposed, allowing
you to intercept, cancel, and modify user edits as they happen. Navigation is
supported with the tab, enter, and arrow keys. The header row is fixed, while the
remaining rolls scroll when necessary, both horizontally and vertically. Figure 2
shows the control.

Figure 2 Editable Grid Control


7

TIBCO General Interface GUI Classes


Exercise 4: Editable Grid 17
|

II. Define Common Patterns


The editable grid class takes advantage of the f o r - e a c h iterator tag. This allows
the template to only specify a bare minimum of markup, while generating a
variable number of rows and columns. Typically, any time you need to create a
control that will display repeating lists of data (such as tables, pick-lists, or grids),
you use a f o r - e a c h iterator.
For example, assume that the following XHTML markup is in your template:
<table>
<tbody>
<tr><td><text>hello</text></td></tr>
</tbody>
</table>

To paint exactly four rows of data, you can implement the following selector
query. Note how the select statement returns an instance of j s x 3 . u t i l . I t e r a t o r.
<table>
<tbody>
<for-each select="jsx3.util.List.wrap([1,2,3,4]).iterator()">
<tr><td><text>hello</text></td></tr>
</for-each>
</tbody>
</table>

Although the above selector hard codes the contents of the iterator (such as
[1,2,3,4]), it is common to use a more dynamic approach for your class. For
example, if your GUI class implements the j s x 3 . x m l . C a c h e a b l e and
j s x 3 . x m l . C D F interfaces (as in this example), the class has access to common
methods such as g e t X M L . This allows you to paint as many rows as you have CDF
records in the XML document used by the control. For example:
<table>
<tbody>
<for-each
select="this.getXML().selectNodeIterator('//record')">
<tr><td><text>hello</text></td></tr>
</for-each>
</tbody>
</table>

Beyond simply iterating over a collection of items, the f o r - e a c h tag also


provides a pointer to the active item in the iteration: $ $ t a r g e t . For example,
assume that you not only want to paint a table row for each record, but you also
want to output the value of the j s x t e x t attribute:
<table>
<tbody>
<for-each
select="this.getXML().selectNodeIterator('//record')">
<var id="mytext">$$target.getAttribute('jsxtext')</var>
<tr><td><text>{mytext}</text></td></tr>
</for-each>

TIBCO General Interface GUI Classes


18 Creating GUI Classes in TIBCO General Interface
|
</tbody>
</table>

You may need to nest f o r - e a c h iterators to handle a variable number of both


rows and columns. For example, the class used for this exercise has a variable
named items that stores column profile information in the following JSON
format:
this.columns = {};
this.columns.items = [{attribute:"jsxid",caption:"ID",width:"25"},
{attribute:"jsxtext",caption:"Name",width:"*"}]

When it is time to paint the component, a corresponding method wraps the array
and returns an iterator.
grid.getIterableColumns = function() {
return jsx3.util.List.wrap(this.columns.items).iterator();
};

Though useful, nested loops introduce a new challenge in that the $ $ t a r g e t


variable is intercepted by the nested (inner) f o r - e a c h . This means that if the
inner loop must access the target of the outer loop, you must cache a pointer to
the $ $ t a r g e t variable before the inner loop begins. For example, note how a
custom variable named m y r e c o r d is used to do this:
<table>
<tbody>
<for-each
select="this.getXML().selectNodeIterator('//record')">
<var id="myrecord">$$target</var>
<tr>
<for-each select="this.getIterableColumns()">
<var
id="mytext">myrecord.getAttribute($$target.attribute)<
/var>
<td><text>{mytext}</text></td>
</for-each>
</tr>
</for-each>
</tbody>
</table>

III. Optimize the Template


An important feature of the Template class is its ability to optimize developer
code. If, for example, a resize event occurs in the browser, the Template will
automatically adjust the size of the control to reflect this change. And if the
Template determines that the resize event did not affect the true size of the
painted instance, it will exit early to spare the unnecessary processing time.
However, there are times when you, the developer, will know of optimizations
that the Template class simply cannot handle given that it is a generic tool. For

TIBCO General Interface GUI Classes


Exercise 4: Editable Grid 19
|

example, the sample class used for this exercise uses a single HTML input in
order to make the grid editable. This reduces the amount of HTML needed to
create a large, editable grid, since the single input can be overlaid on top of a grid
cell when it is time to edit.
The Template engine is capable of positioning the single text input when a given
cell receives focus. The handler for the class locates the position of the cell in
relation to the HTML element that contains both it and the input mask, using the
method g e t R e l a t i v e P o s i t i o n :
var objPos =
jsx3.html.getRelativePosition(objDataContainer,objCell);
objPos.target = objCell;

When the true position for the cell is determined, the position is cached (along
with a few additional values) and the Template engine is notified of the change:
this._setTargetProfile(objPos);

At this point, any template variable that implements the “ b e f o r e e d i t ” trigger is


notified to update its cached value. The Template class then automatically applies
the updated value to any tag in the template that implements the given variable.
For example, consider the following model declarations used by the grid class:
<model>
<var id="mask_value" triggers="beforeedit">
return this._getTargetProfile().value;
</var>
<var id="mask_left" triggers="beforeedit">
return isNaN(this._getTargetProfile().L)? -100
:this._getTargetProfile().L;
</var>
<var id="mask_top" triggers="beforeedit">
return isNaN(this._getTargetProfile().T)? -18
:this._getTargetProfile().T;
</var>
<var id="mask_width" triggers="beforeedit">
return this._getTargetProfile().W || 100;
</var>
<var id="mask_height" triggers="beforeedit">
return this._getTargetProfile().H || 18;
</var>
<var id="mask_display" triggers="beforeedit afteredit">
return this._getTargetProfile().display || "none";
</var>
</model>

When the model variables are updated, the Template class next attempts to apply
these updates to every HTML element that uses one. In this case, the variables
m a s k _ v a l u e , m a s k _ l e f t , m a s k _ t o p , m a s k _ w i d t h , m a s k _ h e i g h t , and
m a s k _ d i s p l a y are used to update the value, display, and position of the single
input box.

TIBCO General Interface GUI Classes


20 Creating GUI Classes in TIBCO General Interface
|

The following input definition appears in the template for the class:
<input u:id="txt_editmask" type="text" class=""
value="{mask_value}"
onkeydown="{onkeydown}" onblur="{onblur}"
style="position:absolute;padding:3 0 0 1;background-color:#ffffff;
z-index:2;color:blue;left:{mask_left};top:{mask_top};
width:{mask_width};height:{mask_height};font-family:{$fontname};
font-size:{$fontsize};display:{mask_display};border:solid 1px
gray;" />

Although the template engine simplifies the act of updating the user interface, it
can also be inefficient, because the engine does not know in advance which tags
implement which variables. When the input mask has been positioned, the engine
continues to scan the remainder of the template, in case other HTML elements
also use the newly-updated variables. To avoid this, you can tell the engine to exit
early when you know that there is no need for it to continue scanning the
remaining HTML elements. For example, note how the i f tag tells the engine to
exit early when there is a flag on the cached profile:
<input u:id="txt_editmask" type="text" class=""
value="{mask_value}"
onkeydown="{onkeydown}" onblur="{onblur}"
style="position:absolute;padding:3 0 0
1;background-color:#ffffff;
z-index:2;color:blue;left:{mask_left};top:{mask_top};

width:{mask_width};height:{mask_height};font-family:{$fontname}
;
font-size:{$fontsize};display:{mask_display};border:solid 1px
gray;" />
<if test="this._getTargetProfile().target"><return/></if>

The final class definition for the editable grid is as follows:


jsx3.require("jsx3.gui.Template","jsx3.xml.Cacheable","jsx3.xml.CD
F");

jsx3.lang.Class.defineClass("my.Grid",jsx3.gui.Template.Block,[jsx
3.xml.Cacheable,jsx3.xml.CDF],function(GRID,grid) {

//init
grid.init = function(strName) {
this.jsxsuper(strName);
};

// defaults
grid.columns = {};
grid.columns.items =
[{attribute:"jsxtext",caption:"Text",width:"*"}];

// template xml
grid.getTemplateXML = function() {
return ['',

TIBCO General Interface GUI Classes


Exercise 4: Editable Grid 21
|
'<transform xmlns="http://gi.tibco.com/transform/"
xmlns:u="http://gi.tibco.com/transform/user" version="1.0">' ,
' <model>' ,
' <var id="mask_value" triggers="beforeedit">return
this._getTargetProfile().value;</var>' ,
' <var id="mask_left" triggers="beforeedit">return
isNaN(this._getTargetProfile().L) ? -100 :
this._getTargetProfile().L;</var>' ,
' <var id="mask_top" triggers="beforeedit">return
isNaN(this._getTargetProfile().T) ? -18 :
this._getTargetProfile().T;</var>' ,
' <var id="mask_width" triggers="beforeedit">return
this._getTargetProfile().W || 100;</var>' ,
' <var id="mask_height" triggers="beforeedit">return
this._getTargetProfile().H || 18;</var>' ,
' <var id="mask_display" triggers="beforeedit
afteredit">return this._getTargetProfile().display ||
"none";</var>' ,
' </model>' ,
' <template dom="static">' ,
' <inlinebox
style="position:{$position};left:{$left};top:{$top};width:{$width}
;height:{$height};margin:{$margin};">' ,
//scrollable data rows
' <div u:id="datarows"
style="position:absolute;left:0px;top:20px;width:100%;height:100%-
20px;overflow:auto;z-index:1;" onscroll="{onscroll}">',
//textbox edit mask
' <input u:id="txt_editmask" type="text" class=""
value="{mask_value}" onkeydown="{onkeydown}" onblur="{onblur}" ',
' style="position:absolute;padding:3 0 0
1;background-color:#ffffff;z-index:2;color:blue;left:{mask_left};t
op:{mask_top};width:{mask_width};height:{mask_height};font-family:
{$fontname};font-size:{$fontsize};display:{mask_display};border:so
lid 1px gray;" />',
//exit the template early if the mask is targeting a
cell; when that happens, merely show the mask above and exit early
' <if
test="this._getTargetProfile().target"><return/></if>' ,
' <table u:protected="true" u:id="datarows_table"
index="0" onmousedown="{onmousedown}"
style="position:absolute;left:0px;top:0px;width:100%;font-size:{$f
ontsize};font-family:{$fontname};table-layout:fixed;"
cellspacing="0" border="0"><tbody u:id="tb_b" u:protected="true">'
,
' <for-each select="this.getIterableRecords()">' ,
' <var id="rowid">this.getId() + \'_\' +
$$target.getAttribute(\'jsxid\')</var>',
' <var id="rowtarget">$$target</var>',
' <tr id="{rowid}" u:id="tr_b" u:protected="true">',
' <for-each select="this.getIterableColumns()">' ,
' <var
id="celltext">rowtarget.getAttribute($$target.attribute) ||
"&amp;#160;"</var>',
' <var id="columnwidth">$$target.width</var>',
' <td u:id="td_b" u:protected="true"
style="width:{columnwidth};padding:3px;border-right:solid 1px

TIBCO General Interface GUI Classes


22 Creating GUI Classes in TIBCO General Interface
|
lightblue;border-bottom:solid 1px
lightblue;"><text>{celltext}</text></td>',
' </for-each>',
' </tr>',
' </for-each>',
' </tbody></table>',
' </div>',
//fixed header row
' <div u:id="header"
style="position:absolute;left:0px;top:0px;width:100%;height:20px;b
order:{myborder|solid 1px
gray};z-index:2;background-color:{headerbg};color:{headercolor};ov
erflow:hidden;">',
' <table u:protected="true" u:id="header_tbl"
style="position:absolute;left:0px;top:0px;width:100%;font-size:{$f
ontsize};font-weight:bold;table-layout:fixed;" cellpadding="3"
cellspacing="0" border="0"><tbody u:id="tb_h" u:protected="true">'
,
' <tr u:id="tr_h" u:protected="true">',
' <for-each select="this.getIterableColumns()">' ,
' <var id="headertext">$$target.caption</var>',
' <var id="columnwidth">$$target.width</var>',
' <td u:id="td_h" u:protected="true"
style="width:{columnwidth};white-space:nowrap;text-align:center;">
',
' <text>{headertext}</text>',
' </td>',
' </for-each>',
' </tr>',
' </tbody></table>',
' </div>',
' </inlinebox>' ,
' </template>' ,
'</transform>'].join("");
};

// 6) define the CONTROLLER functions


grid.onmousedown = function(objEvent,objGUI) {

this.onbeforeedit(objEvent,objEvent.srcElement().parentNode,objEve
nt.srcElement());
};

grid.onscroll = function(objEvent,objGUI) {
//called when the data rows are scrolled
this.getRenderedBox("header_tbl").style.left =
-objGUI.scrollLeft + "px";
};

grid.onblur = function(objEvent,objGUI) {
//handle the primitive event (onblur) with the prototype
(instance) event
this.onafteredit(objEvent,objGUI);
};

TIBCO General Interface GUI Classes


Exercise 4: Editable Grid 23
|

grid.onkeydown = function(objEvent,objGUI) {
var objCell = this._getTargetProfile().target;
var intCellIndex = objCell.cellIndex
var objRow;

if((objEvent.enterKey() && !objEvent.shiftKey()) ||


objEvent.downArrow()) {
//commit the value; advance edit to the next cell down
this.onafteredit(objEvent,objGUI);

//if the current row isn't the last row, apply edit mask to
the next row down
objRow = (objCell.parentNode !=
objCell.parentNode.parentNode.lastChild) ?
objCell.parentNode.nextSibling :
objCell.parentNode.parentNode.firstChild;
} else if(objEvent.upArrow() || (objEvent.enterKey() &&
objEvent.shiftKey())) {
//commit the value; advance edit to the next cell up
this.onafteredit(objEvent,objGUI);

//if the current row isn't the first row, apply edit mask to
the next row up;
objRow = (objCell.parentNode !=
objCell.parentNode.parentNode.firstChild) ?
objCell.parentNode.previousSibling :
objCell.parentNode.parentNode.lastChild;
} else if(objEvent.rightArrow()) {
//commit the value; advance edit to the next cell down
this.onafteredit(objEvent,objGUI);
var objRow = objCell.parentNode;
intCellIndex = objRow.lastChild == objCell ? 0 :
intCellIndex+1;
} else if(objEvent.leftArrow()) {
//commit the value; advance edit to the next cell up
this.onafteredit(objEvent,objGUI);
var objRow = objCell.parentNode;
intCellIndex = objRow.firstChild==objCell
?objRow.lastChild.cellIndex :
intCellIndex-1;
}

//begin the edit session for the target cell


if(objRow)

this.onbeforeedit(objEvent,objRow,objRow.childNodes[intCellIndex])
;
};

grid._getTargetProfile = function() {
//when an edit event happens, a target profile is created that
describes the context
return this._jsxtargetprofile || {};
};

TIBCO General Interface GUI Classes


24 Creating GUI Classes in TIBCO General Interface
|

grid._setTargetProfile = function(objP) {
//when an edit event happens, a target profile is created that
describes the context
this._jsxtargetprofile = objP;
};

grid.onbeforeedit = function(objEvent,objRow,objCell) {
//use a sleep delay to stop repeated clicks and key strokes
from taxing performance
jsx3.sleep(function() {

this._onbeforeedit(objEvent,objRow,objRow.childNodes[objCell.cellI
ndex]);
},"my.GRID",this);
};

grid._onbeforeedit = function(objEvent,objRow,objCell) {
//get the id for the row that was clicked
var strCdfId =
objRow.getAttribute("id").substr(this.getId().length+1);
var strAttName =
this.columns.items[objCell.cellIndex].attribute;
var strValue =
this.getRecordNode(strCdfId).getAttribute(strAttName);

//allow the before-edit event to be cancelled


var objReturn = this.doEvent(jsx3.gui.Interactive.BEFORE_EDIT,

{objEVENT:objEvent,strID:strCdfId,objCELL:objCell,strVALUE:strValu
e});
if(objReturn !== false) {
//determine information about the target cell being edited
(left, top, etc)
var objThis = this.getRendered(objRow);
var objDataContainer =
this.getRenderedBox("datarows",objThis);
var objMask = this.getRenderedBox("txt_editmask",objThis);

//query the system for the location of the target table cell
var objPos =
jsx3.html.getRelativePosition(objDataContainer,objCell);
//when running on firefox, builds earlier than 3.6.1 have a
bug
if(!(jsx3.getVersion() < "3.6.1" &&
/fx/.test(jsx3.CLASS_LOADER.getType()))) {
objPos.L = objPos.L + objDataContainer.scrollLeft;
objPos.T = objPos.T + objDataContainer.scrollTop;
}

objPos.value = strValue || "";


objPos.display = "block";
objPos.target = objCell;
objPos.id = strCdfId;

TIBCO General Interface GUI Classes


Exercise 4: Editable Grid 25
|
objPos.attribute = strAttName;

//cache the information about the target


this._setTargetProfile(objPos);
this.syncProperty("beforeedit",true);
//give cursor focus to the edit mask (the text input)
objMask.focus();
}
};

grid.onafteredit = function(objEvent,objGUI) {
//get the profile object
var objP = this._getTargetProfile();

//get the new value entered by the user


var objReturn;
if(objP.value != objGUI.value) {
//allow the edit to be cancelled/modified
objReturn = this.doEvent(jsx3.gui.Interactive.AFTER_EDIT,
{objEVENT:objEvent,strID:objP.id,objCELL:objP.target,
strVALUE:objGUI.value});
if(objReturn !== false) {
//update the data model

this.insertRecordProperty(objP.id,objP.attribute,objGUI.value,fals
e);

//update the view


objP.target.innerHTML = jsx3.util.strEmpty(objGUI.value) ?
"&#160;" : objGUI.value;
}
}

//reset the mask (hide it)


this._setTargetProfile({value:""});
this.syncProperty("afteredit",true);

//fire the final commit event (not cancellable)


if(objReturn !== false)
this.doEvent(jsx3.gui.Interactive.AFTER_COMMIT,
{objEVENT:objEvent,strID:objP.id,objCELL:objP.target,
strVALUE:(objReturn != null && objReturn.strVALUE ?
objReturn.strVALUE : objGUI.value)});
};

grid.setColumns = function(arrColumns) {
//call to change the columns to render for the table. The
schema is as follows.:
this.columns.items = arrColumns;
//the GI serializer only saves scalar types
this.encodeColumns(arrColumns);
this.repaint();
};

TIBCO General Interface GUI Classes


26 Creating GUI Classes in TIBCO General Interface
|
grid._getColumns = function(arrColumns) {
return this._columns || "";
};

grid.encodeColumns = function(arrColumns) {
//serialize the columns array as a string so the GI class
serializer
var a = [];
for(var i=0;i<arrColumns.length;i++) {
var cur = arrColumns[i];
a.push("{attribute:\"" + cur.attribute + "\",caption:" +
jsx3.util.strEscapeJSON(cur.caption) + ",width:\"" + cur.width +
"\"}");
}
this._columns = "[" + a.join(",") + "]";
};

grid.onAfterAssemble = function() {
//when a serialized object is restored in GI (deserialized),
this method is called;
if(!jsx3.util.strEmpty(this._columns))
this.columns.items = jsx3.eval(this._columns);
};

grid.getIterableRecords = function() {
var objCDF = this.getXML();
return objCDF ? objCDF.selectNodeIterator("//record") :
(new jsx3.util.List([])).iterator();
};

grid.getIterableColumns = function() {
return new jsx3.util.List(this.columns.items.length ?
this.columns.items : []).iterator();
};

});

TIBCO General Interface GUI Classes


System Resolvers 27
|

Appendix A System Resolvers

System resolvers help simplify authoring HTML templates by obviating the need
to declare local variables. Table 5 describes the system resolvers, the instance
property to which they map, and relevant triggers.

Table 5 System Resolvers

Name Type Description Trigger


$id attribute Renders the j s x i d field as i d = " { $ i d } " .
Always applied to the root node in the
template regardless of developer choice.
This ensures that the model and view are
always synchronized.

$label attribute Renders the j s x n a m e field as jsxname


label="{$label}"

$position box Renders the jsxrelativeposition field as jsxrelativeposition


position:{$position};

$left box Renders the j s x l e f t field as jsxleft


l e f t : { $ l e f t } ; when the position is
absolute and when specified

$top box Renders the j s x t o p field as t o p : { $ t o p } ; jsxtop


when the position is absolute and when
specified

$width box Renders the j s x w i d t h field as jsxwidth


width:{$width}

$height box Renders the j s x h e i g h t field as jsxheight


height:{$height}

$zindex css Renders the j s x z i n d e x field as jsxzindex


z-index:{$zindex}

$color css Renders the j s x c o l o r field as jsxcolor


color:{$color}

$fontname css Renders the j s x f o n t n a m e field as jsxfontname


font-family:{$fontname}

TIBCO General Interface GUI Classes


28
| Appendix A System Resolvers

Table 5 System Resolvers (Cont’d)

Name Type Description Trigger


$fontsize css Renders the j s x f o n t s i z e field as jsxfontsize
font-size:{$fontsize}

$fontweight css Renders the j s x f o n t w e i g h t field as jsxfontweight


font-weight:{$fontweight}

$display css Renders the j s x d i s p l a y field as jsxdisplay


display:{$display}

$visibility css Renders the j s x v i s i b i l i t y field as jsxvisibility


visibility:{$visibility}

$style-group box Renders the value of the jsxstyleoverride


j s x s t y l e o v e r r i d e field at the end of the
existing style declaration. This provides a
mechanism for injecting multiple styles into
a single tag.

$classname attribute Renders the j s x c l a s s n a m e field as jsxclassname


class="{$classname}".
If the given class has a static field named
D E F A U L T C L A S S N A M E , the value of this field
will be appended, meaning a class like
j s x 3 . g u i .B l o c k that implements a
j s x c l a s s n a m e property with the value,
" c u s t o m s t y l e " , would render as
class="jsx30block customstyle"

$bgcolor css Renders the value of the j s x b g c o l o r field jsxbgcolor


as b a c k g r o u n d - c o l o r : { $ b g c o l o r } ;

$bg css Renders the value of the j s x b g field as jsxbgcolor jsxbg


background:{$bg};

$padding box Renders the value of the j s x p a d d i n g field jsxpadding


as p a d d i n g : { $ p a d d i n g } ;

$margin box Renders the value of the j s x m a r g i n field jsxmargin


as m a r g i n : { $ m a r g i n } ;

$border box Renders the value of the j s x b o r d e r field jsxborder


as b o r d e r : { $ b o r d e r } ;

$textalign css Renders the value of the j s x t e x t a l i g n jsxtextalign


field as t e x t - a l i g n : { $ t e x t a l i g n } ;

TIBCO General Interface GUI Classes


System Resolvers 29
|

Table 5 System Resolvers (Cont’d)

Name Type Description Trigger


$overflow css Renders the value of the j s x o v e r f l o w jsxoverflow
field as o v e r f l o w : { $ o v e r f l o w } ;

$attribute-group box Similar to $ s t y l e - g r o u p , this resolver


allows a list of attributes to be injected into a
given tag. This is a special type of resolver
that must be declared as a child of the target
HTML node, since it affects the attribute list
for the node. For example,

<div value="12"
style="color:red;">

<attribute-group>{$attribute-gro
up}</attribute-group>
</div>

Any attribute on a General Interface object,


using the API, s e t A t t r i b u t e will be
reflected by this resolver.

$tagname box Allows the HTML tag name to be updated. jsxtagname


This is a special type of resolver that must
be declared as a child of the target HTML
node, since it affects the tag name of the
given node. For example,
<span style="color:red;">
<tagname>{$tagname}</tagname>
</span>

$text box Renders the value of the jsxtext field as a jsxtext


text node child of the given HTML node.
This is a special type of resolver that must
be contained by a < t e x t / > node. For
example,
<span style="color:red;">
<text>{$text}</text>
</span>

$index attribute Renders the value of the j s x i n d e x field as jsxindex


t a b i n d e x = " { $ i n d e x } " . If this field has
no value, the default (0) will be used

$tip attribute Renders the value of the j s x t i p field as jsxtip


title="{$tip}".

TIBCO General Interface GUI Classes


30
| Appendix A System Resolvers

Table 5 System Resolvers (Cont’d)

Name Type Description Trigger


$disabled attribute Renders the value of the j s x e n a b l e d field jsxenabled
as d i s a b l e d = " { $ d i s a b l e d } " .

$onblur event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onchange event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onclick event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$ondblclick event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onfocus event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onkeydown event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onkeypress event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onkeyup event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onmousedown event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onmousemove event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onmouseout event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

TIBCO General Interface GUI Classes


System Resolvers 31
|

Table 5 System Resolvers (Cont’d)

Name Type Description Trigger


$onmouseover event Renders the given event listener on the jsxenabled
HTML tag if the control is not set to
jsxenabled = false;

$onmouseup event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

$onmousewheel event Renders the given event listener on the jsxenabled


HTML tag if the control is not set to
jsxenabled = false;

TIBCO General Interface GUI Classes


32
| Appendix B Control Statements

Appendix B Control Statements

When authoring a template, you use a combination of HTML and control


statements to help with conditional processing (f o r - e a c h and i f - e l s e ). You can
use any HTML node such as span or div where noted below. Any node not listed
by name in Table 6 is assumed to be an HTML node that should be painted.

Table 6 Control Statements

Node Name Can be a Parent of Description


transform model | template | The root node for the template. Declare as follows (the namespace
@xmlns | @xmlns:u | declarations must be included, but are removed in some of the
@version
usage examples for readability):
<transform
xmlns="http://gi.tibco.com/transform/"
xmlns:u="http://gi.tibco.com/transform/user"
version="1.0"></transform>

@xmlns http://gi.tibco.co This is the namespace declaration for the template. All element
m/t nodes within the template belong to this namespace.
ransform/

@xmlns:u http://gi.tibco.co Use to add custom attributes to a given HTML tag in your
m/t template without having the given attribute render to the view.
ransform/user

version 1.0 This is the template version. General Interface release 3.6
supports version 1.0.

model var | import Contains all locally declared variables (resolvers) used by the
template when painting. It is unnecessary to declare this tag if
you do not need any special formatting for your template.

TIBCO General Interface GUI Classes


Control Statements 33
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


var[parent #text | @id | @name Declares a variable resolver in the model. Although it is often
::model] | easier to implicitly define resolvers by simply using them within
@type | @triggers |
the template, it is sometimes useful to explicitly declare them in
@defaultvalue
the model in order to retain full control over how the given
resolver is run. For example:

<transform>
<model>
<var id="mycolor" name="background-color"
type="css" triggers="jsxcolor"
defaultvalue="red">
return this.jsxcolor;
</var>
</model>
<template>
<span style="
background-color:{mycolor};"><text>whirled
peas</text></span>
</template>
</transform>

@id #text This is the ID for the variable. It should be unique for the
template. It should not begin with a $ , because the system owns
this prefix to identify its own variable resolvers.

var[parent #text The name attribute is used when the variable is rendered as final
::model]/@ HTML. For css and box type variables, this is the CSS property
name
name. For attributes and events, this is the attribute or event
name. For example, this structure

<transform>
<model>
<var id="mycolor" name="background-color"
type="css" triggers="jsxcolor"
defaultvalue="red">
return this.jsxcolor;
</var>
</model>
<template>
<span style="
background-color:{mycolor};"><text>whirled
peas</text></span>
</template>
</transform>

generates this HTML:


<span style=" background-color:red;">whirled
peas</span>

TIBCO General Interface GUI Classes


34
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


var[parent box | attribute | The variable type lets the template engine know how a given
::model]/@ event | css variable should be projected into the view. For example, to add a
type (default)
src attribute to an img tag, declare its type as attribute:

<transform>
<model>
<var id="url" name="src" type="attribute"
triggers="url">
return this.url;
</var>
</model>
<template>
<img src="{url};"/>
</template>
</transform>

The template engine also allows for implicit variable declarations


by simply using a variable inline. For example, the following
syntax is exactly equivalent to the explicit declaration above:

<transform>
<template>
<img src="{url};"/>
</template>
</transform>

TIBCO General Interface GUI Classes


Control Statements 35
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


var[parent #text Template variables can implement a triggers field to let the
::model]/@ template engine know which updates to the model should trigger
triggers
changes in the view. For example, when updating the u r l
property on a GUI object (t h i s . u r l = ' x ' ), a controller
function can call s e t P r o p e r t y . This compound method first
updates the model with the new value and then triggers an
update to the view to ensure that the model and view remain
synchronized.

function setURL(strURL) {
this.setProperty("url", strURL);
}

Given the method call above, the following template would be


triggered to update the u r l variable. The i m g tag would then be
updated to reflect the new u r l value o n - s c r e e n .

<transform>
<model>
<var id="url" name="src" type="attribute"
triggers="url">
return this.url;
</var>
</model>
<template>
<img src="{url};"/>
</template>
</transform>

Note: The triggering system is many-to-many. A single template


variable can implement multiple triggers and a single trigger can
be applied to multiple variables. For example, in the following
template both variables are updated when the u r l trigger is
fired, because both implement the u r l trigger.
<transform>
<model>
<var id="url" name="src" type="attribute"
triggers="url triggera tribbgerb">return
this.url;</var>
<var id="fullurl" name="path"
type="attribute" triggers="url">return
this.fullurl;</var>
</model>
<template>
<img src="{url};" path="{fullurl}"/>
</template>
</transform>

TIBCO General Interface GUI Classes


36
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


var[parent #text The d e f a u l t v a l u e field is used to assign a variable default
::model]/@ when its resolver function returns null. Therefore, the following
defaultval
two variable declarations are equivalent:
ue
<var id="url" name="src" type="attribute">
return this.url != null ? this.url :
"abc.gif";
</var>

or

<var id="url" name="src" type="attribute"


defaultvlaue="abc.gif">
return this.url;
</var>

Default values can also be implicitly declared inline within the


body of the template using a pipe character (" | " ). Therefore, the
following implicit declaration is equivalent to the two explicit
declarations above:

<img src="{url|abc.gif}"/>

If no d e f a u l t v a l u e field is used and the resolver function


returns null, it will result in the given property being removed
from the object when painted, while an empty string results in an
empty value. For example, a variable named ' s r c ' that returns
null (and with no d e f a u l t v a l u e field) would result in the
following HTML:

<img/>

While an empty string would be expressed as


follows:

<img src=""/>

TIBCO General Interface GUI Classes


Control Statements 37
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


import @resolver | @set | This allows for re-use of common resolvers and is used to import
@library variable resolvers declared and used by other widget libraries
and classes. . For example, the following statement imports the
variable resolver named $ c o l o r :

<import resolver="$color"/>

Variable resolvers can belong to a namespace. If you create


a variable named " c o l o r " and the resolver belongs to the
class my.Spinner, then another class can import the variable
resolver by using the following declaration:
<import resolver="color" set="my.Spinner"/>
To import every resolver from another class, pass the name of the
class as the library name:

<import library="my.Spinner"/>

System resolvers (those beginning with " $ " ) do not need to be


imported. This means that simply using a system resolver is the
appropriate way to declare its import. For example, note how the
u r l resolver must be explicitly imported from the class named
m y . S p i n n e r , while the system resolver, $ p o s i t i o n , does not:

<transform>
<model>
<import resolver="color" set="my.Spinner"/>
</model
<template>
<img src="{url};"
style="position:{$position}"/>
</template>
</transform>

template HTML | Contains the HTML template


@onbeforeinit |
@oninit |
@onbeforepaint |
@onpaint |
@onbeforeresize |
@onresize |
@onbeforeresizechi
ld | @dom |
@recalc

@onbeforei text/javascript Called immediately before the template profile is created for a
nit given GUI widget. The template profile is a list of variables that
are merged with the template to render the final HTML.

TIBCO General Interface GUI Classes


38
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


@oninit text/javascript Called immediately after the templates profile is
created

@onbeforep text/javascript Called immediately before paint. If this method returns an


aint alternate string of text/HTML, the control will not attempt to
paint and will instead use the provided string.

@onpaint text/javascript Called after the HTML has been painted and rendered by the
browser. Called in a separate call stack from the paint method.

@onbeforer text/javascript Called immediately before a resize event. Return a Boolean false
esize to cancel the resize for the control.

@onresize text/javascript Called immediately after a resize event. The object's dimensions
will have been updated in both the model and view, but its
descendant objects may not have been resized yet.

@onbeforer text/javascript Called immediately before a child is resized. The method will be
esize passed one parameter: a reference to the child about to be resized.
child
Return a Boolean False to cancel the resize for the child.

@recalc true|false Called immediately after the HTML has been painted and
(default) rendered by the browser, causing the resize engine to apply any
dynamic position calculations to the painted control. Use this
feature in your template if you declare any < v a r > elements
within the < t e m p l a t e > section that affect box properties like
left, top, width, padding, border, etc.

TIBCO General Interface GUI Classes


Control Statements 39
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


@dom dynamic | static Some templates result in different HTML output. For example, a
(default) template with i f /e l s e conditional statements, may create an
on-screen DOM that differs each time it is rendered. In such a
situation, specify that the HTML content is dynamic and
therefore must be located using named IDs. In other words when
the DOM is dynamic, then each HTML node in the template
needs a corresponding unique ID, using the user namespace. For
example:

<template dom ="dynamic">


<span u:id="someid">
<if-else>
<if test="isInput()">
<input u:id="myinput" value="{mytext}"
type="text"/>
</if>
<else>
<span
u:id="myspan"><text>{mytext}</text></span>
</else>
</if-else>
</span>
</template>

Note: If your template uses a for-each iterator, it must use a static


DOM.

TIBCO General Interface GUI Classes


40
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


var[ancest text/javascript | Declares a local variable within the body of the template. This
or::templa @id allows a given variable to be dynamically updated during
te]
execution. For example:

<for-each select="this.getMyRows()">
<var
id="rowid">$$target.getAttribute(\'jsxid\')</va
r>
<var
id="rtext">$$target.getAttribute(\'jsxtext\')</
var>
<div id="{rowid}"><text>{rtext}</text></div>
</for-each>

var declarations in the body of the template execute


differently than v a r declarations made within the model.
This means that a variable that returns the color r e d would
be declared like this, if declared within the model:
<transform>
<model>
<var id="abc">return "red"</var>
</model>
<template>
<span><text>{abc}</text></span>
</template>
</transform>

However, when declared within the template, a local variable


declaration will not be stored as a global function, and therefore
should not use a return statement:

<transform>
<template>
<var id="abc">"red"</var>
<span>
<text>{abc}</text>
</span>
</template>
</transform>

TIBCO General Interface GUI Classes


Control Statements 41
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


HTML HTML | for-each | Any valid HTML tag can be used in a template. All attributes on
attach | var | the tag will be rendered on the tag except for those belonging to
drawspace | if |
the http://gi.tibco.com/transform/user namespace.
if-else | left |
top | width |
height |
position | border |
padding | margin |
text | empty |
attribute-group |
style-group | @u:id
| @u:protected

@u:protect true Use on an HTML tag to denote that its dimensions should not be
ed managed by the General Interface layout engine. The tag can still
use the replacement variable engine, but its layouts will be
unaffected. For example, the following template:

<input u:protected="true"
style="width:100%;height:100%;color:{mycolor};"
/>
results in the following HTML when painted

<input
style="width:100%;height:100%;color:red;"/>

However, if the u : p r o t e c t e d attribute had not been used, the


layout engine would have replaced the percentages with absolute
pixel values to reflect the dimensions allowed by the containing
HTML tag. For example,:

<input
style="width:80px;height:235px;color:red;"/>

TIBCO General Interface GUI Classes


42
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


left text/javascript Can be used to specify the left position of an HTML node. For
example:

<span style="position:absolute;">
<left>$$parentwidth - 10</left>
</span>

Note: The syntax above is not required, but can be used for
greater control over layout, since it accepts any valid JavaScript
statement. However, it generally more common to simply declare
the style inline and have the system automatically handle the
calculation. For example, note how the CSS accepts the calculated
value, 100% - 10:

<span
style="position:absolute;left:100%-10px;"></spa
n>

Replacement variables can be used in both


situations. Whether expanded:

<span style="position:absolute;">
<left>{$left}</left>
</span>

or inline:

<span
style="position:absolute;left:{$left};"></span>

Two custom style properties are also provided for convenience


when doing calculations: right and bottom. These simplify left
and top declarations that use calculations. For example, the
following two style declarations are equivalent:

<span
style="position:absolute;right:10px;"></span>

or

<span
style="position:absolute;left:100%-10px;"></spa
n>

top text/javascript See the description for left.

TIBCO General Interface GUI Classes


Control Statements 43
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


width text/javascript See the description for left.

height text/javascript See the description for left.

TIBCO General Interface GUI Classes


44
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


position absolute | relative Can be used to specify the relative/absolute position of an HTML
(default) node. For example:

<input style="color:blue;">
<position>relative</position>
</input>

The template engine does not required the expanded syntax, but
can be used for greater control over layout, since it accepts any
valid JavaScript statement. However, it is generally more
common to simply declare the style inline and have the system
automatically handle the calculation. For example:

<input style="position:relative;color:blue"/>
Replacement variables can be used in both situations. Whether
expanded:

<input style="color:blue;">
<position>{$position}</position>
</input>

or inline:

<input
style="position:{$position};color:blue;"/>

Unlike the calculated fields, l e f t , t o p , w i d t h , and h e i g h t , this


field is treated as a string, assuming the value is either relative,
absolute, or the value of a given replacement variable. Only
relative and absolute are accepted as values by the layout engine;
however, the variable replacement engine accepts any value. For
example, note how the type attribute is declared as c s s (not box)
and the specific node is specified as protected (u : p r o t e c t e d ) :

<transform>
<model>
<var id="myposition" type="css"
name="position">return "fixed";</var>
</model>
<template>
<div u:protected="true"
style="position:{myposition};">
<text>{abc}</text>
</div>
</template>
</transform>

TIBCO General Interface GUI Classes


Control Statements 45
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


empty true Use when a given node should render as an empty node. Note
that i m g , b r , and i n p u t nodes are automatically tagged and
need not specify this value. For example:

<input type="text">
<empty>true</empty>
</input>
will render as:

<input type="text"/>

attribute-grou #text Use to inject a group of attributes (that is, s r c = " a . g i f "
p w i d t h = " 1 " ) into an HTML
node in the template. For example:

<img>
<attribute-group>{myAttListVar}</attribute-grou
p>
</img>
will render as:

<img src="a.gif" width="1"/>

style-group #text Similar to attribute-group. Use to inject a group of CSS styles


(that is, c o l o r : b l u e ; f o n t - w e i g h t : b o l d ; ) into an HTML
node in the template. Consider the expanded syntax:

<img>
<style-group>{someVarWithStyles}</style-group>
</img>

And the inline syntax:

<img
style="font-size:10px;style-group:{someVarWithS
tyles};"/>

This is how the tag renders:

<img style="
font-size:10px;color:blue;font-weight:bold ;"/>

TIBCO General Interface GUI Classes


46
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


border #text See the description for position. The value for this node is best
expressed in the simplified General Interface notation, using the
pattern, “ s t y l e - w i d t h - c o l o r ” , as the four compass positions
are defined for the border, beginning with north. For example:

<span>
<border>solid 1px red;dashed 2px red;0px;solid
1px red;</border>
</span>

Note that when using a replacement variable for the border, it is


easier to simply declare it inline:

<span style="border:{$border};"></span>

Due to the way that the template engine parses styles, you should
not specify complex border declarations or CSS border variants
such as b o r d e r - t o p and b o r d e r - c o l o r inline:

<span style=”border:0px;solid 1px


gray;0px;0px;”/>

However, you can implicitly define a variable as a way to inline a


complex style. Note that the pipe character ( “|”) is used to
separate the named variable from its default value (the value to
use if the given variable returns null). By wrapping the complex
border declaration within a variable, the template engine will
parse the values correctly. The variable name in such cases is
unimportant and need only be unique among other variables
declared in the template.

<span style="border:{myborder|0px;solid 1px


gray;0px;0px;};"></span>

TIBCO General Interface GUI Classes


Control Statements 47
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


padding #text See the descriptions for position and border. Also note that the
value for this node is best expressed in the simplified General
Interface notation, using implied pixel units. For example, to
specify 8 pixel padding, use 8. To specify each side individually,
use the four compass positions starting at north and moving
clockwise. For example:

<span>
<padding>8 2 2 8</padding>
</span>

Or inline:

<span style="padding:8 2 2 8;"></span>

CSS syntax can be used inline, but it must use pixels and it must
not use any padding variants such as p a d d i n g - t o p a n d
p a d d i n g - l e f t . For example:

<span style="padding:8px;"></span>

margin #text See the description for padding.

tagname #text See the description for position. Use to dynamically replace the
tagname for a given HTML node. For example, if a given node
should render as a DIV or a SPAN, use the following to allow this
property to be dynamic, using a replacement variable:

<span>
<tagname>{$tagname}</tagname>
</span>

text #text Use to wrap any text content in your template. All text content
must come first before any descendant nodes and must be
wrapped in a single text tag. For example, this template structure:

<span>
<text>Hello world</text>
</span>

renders as the following HTML:

<span>Hello world</span>

TIBCO General Interface GUI Classes


48
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


for-each HTML| var | for-each Use to add an iterator to the template. For example, assume the
| drawspace | if | given template should output a table row for each CDF record in
its cached XML document:
if-else | @select
<table>
<tbody>
<for-each
select="this.getIterableRecords()">
<var
id="myid">$$target.getAttribute("jsxid")</var>
<var
id="mytext">$$target.getAttribute("jsxtext")</v
ar>
<tr
id="{myid}><td><text>{mytext}</text></td></tr>
</for-each>
</tbody>
</table>

Note that the select statement must return an instance of


j s x 3 . u t i l . I t e r a t o r . For example, the
g e t I t e r a b l e R e c o r d s method (a controller method) can be
expressed as follows:
widget.getIterableRecords = function() {
var objCDF = this.getXML();
return objCDF ?
objCDF.selectNodeIterator("//record") : (new
jsx3.util.List([])).iterator();
};

attach drawspace | @select Use to attach descendant content. Similar to a for-each iterator,
the select statement expects an instance of
j s x 3 . u t i l . I t e r a t o r . The context variable, $ $ t a r g e t is
available within the attach tag, meaning any variable declaration
can access the current item in the iterator by using it. For
example:

<div>
<attach select="(new
jsx3.util.List(this.getChildren())).iterator()"
>
<drawspace top="$$target.getChildIndex() *
8"/>
</attach>
</div>

TIBCO General Interface GUI Classes


Control Statements 49
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


drawspace @parentwidth | Use to modify the drawspace. A drawspace tag can either be a
@parentheight | @left child of an attach tag (as a way to modify the default drawspace
that would be sent to a given child). The drawspace tag can also
|@top | @width |
be used. See attach and @left for usage examples.
@height | @position

continue Use inside of a conditional tag (i f /i f - e l s e ) to skip execution


of a given iteration. For example, the following skips a f o r - e a c h
iteration whenever the given target (a CDF record) does not
implement a j s x i d attribute.

<table>
<tbody>
<for-each
select="this.getIterableRecords()">
<var
id="myid">$$target.getAttribute("jsxid")</var>
<if
test="jsx3.util.strEmpty(myid)"><continue/></if
>
<var
id="mytext">$$target.getAttribute("jsxtext")</v
ar>
<tr
id="{myid}><td><text>{mytext}</text></td></tr>
</for-each>
</tbody>
</table>

break Similar to continue, except that the entire loop is exited early, not
just the iteration.

TIBCO General Interface GUI Classes


50
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


return Similar to break and continue, except that the function is
completely exited. Useful for intercepting normal processing of a
template and exiting early for improved runtime performance. A
local variable, M O D E , defines the mode the template is being run
in. This variable can be queried at any time to know which action
is being invoked by the template engine:

paint, update, create, dimension, and paintchild

For example, you can abort the update event for your template
(assuming you know that there is no reason to continue resizing
descendant objects):

<if test="MODE=='update' &amp;&amp;


someCondition()">
<return/>
</if>

if-else if | else Use to wrap a complex conditional (choice). For example:

<if-else>
<if test="somevalue == 1">
<span><text>a</text></span>
</if>
<if test="somevalue == 2">
<span><text>b</text></span>
</if>
<else>
<span><text>c</text></span>
</else>
</if-else>

if HTML | break | Use to wrap a simple condition. For example:


continue | var |
<if test="$$target==false">
for-each | attach | <break/>
drawspace | if | </if>

if-else | return | @test

else HTML | break | See the description for i f - e l s e .


continue | var |
for-each | attach |
drawspace | if |
if-else | return

TIBCO General Interface GUI Classes


Control Statements 51
|

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


@parentwidth text/javascript Sets the parentwidth on the drawspace. The value is calculated
and accepts any valid JavaScript. For example, to tell all children
that their allowed drawspace is exactly 100 pixels wide, use the
following:

<div>
<attach select="(new
jsx3.util.List(this.getChildren())).iterator()"
>
<drawspace parentwidth="100"/>
</attach>
</div>

@parentheight text/javascript See the description for @parentwidth.

@left text/javascript The attributes, left, top, width and height, border, margin,
position, and padding, can be set on a drawspace node to force
the next HTML node into a particular position, regardless of its
setting. For example, the following SPAN will be placed at left
position 12, even though it specifies that it render at position 10:

<div style="width:100%;height:100%;">
<drawspace left="12"/>
<span style="left:10px;"></span>
</div>

@top text/javascript See the description for @left.

@width text/javascript See the description for @left.

@height text/javascript See the description for @left.

@border text/javascript See the description for @left. In the following example, note how
the border is wrapped in apostrophes, because it is a string:

<div>
<attach select="(new
jsx3.util.List(this.getChildren())).iterator()"
>
<drawspace border="'solid 1px gray'"/>
</attach>
</div>

@margin text/javascript See the description for @left.

@position text/javascript See the description for @left.

@padding text/javascript See the description for @left.

TIBCO General Interface GUI Classes


52
| Appendix B Control Statements

Table 6 Control Statements (Cont’d)

Node Name Can be a Parent of Description


@padding text/javascript See the description for @left.

TIBCO General Interface GUI Classes


Contextual Variables 53
|

Appendix C Contextual Variables

When the template is run, the system provides contextual variables to give
additional information that is available only when the template is run. For
example, because the system manages layouts, the drawspace changes
(decreases) as each nested tag claims its dimensions. In this example, the
$ $ p a r e n t w i d t h field is used to define how much width is available to render
within. Each variable listed in Table 7 is available within the < t e m p l a t e / > section.
This means if you declare a variable (< v a r / > ) or drawspace (< d r a w s p a c e / > ), you
can use this value in your calculations.

Table 7 Contextual Variables

Name Description Sample Use


$$parent provides the width of the Force the next HTML tag to be
width containing drawspace rendered to be half as small as the
available width:
<drawspace
width="$$parentwidth *
.5"/>

$$parent provides the height of the Render the HTML tag10 pixels from
height containing drawspace the bottom of its container:
<span
style="position:absolute;">
<top>$$parentheight -
10</top>
<span>

The syntax above is not required, but


you can use it for greater control
over layout. It is more common to
simply declare the style inline and
have the system automatically
handle it, as in the following
example:
<span
style="position:absolute;to
p:100%-10;"></span>

TIBCO General Interface GUI Classes


54
| Appendix C Contextual Variables

Table 7 Contextual Variables (Cont’d)

Name Description Sample Use


$$target provides a pointer to the active Loop through each CDF record and
element within a given for-each or render a div for each one. Use the
attach iteration. If nested loops are jsxid from each record in the
used within the template, it is iteration to generate the id for each
possible to cache this value to div and the j s x t e x t field for the
avoid collisions div's text content:
<for-each
select="this.getMyRows()">
<var
id="rowid">$$target.getAttr
ibute(\'jsxid\')</var>
<var
id="rowtext">$$target.getAt
tribute(\'jsxtext\')</var>
<div
id="{rowid}"><text>{rowtext
}</text></div>
</for-each>

A locally declared variable can be


used to cache the $ $ t a r g e t when
doing nested iterations:
<for-each select="this.
getMyRows()">
<var
id="rowid">$$target.getAttr
ibute(\'jsxid\')</var>
<var
id="mytarget">$$target</var
>
<div id="{rowid}">
<for-each select="this.
getMyCols()">
<var
id="rowtext">mytarget.getAt
tribute(\'jsxtext\')</var>

<text>{rowtext}</text>
</for-each>
</div>
</for-each>

TIBCO General Interface GUI Classes

Das könnte Ihnen auch gefallen