Chapter 29. Methods, Events, Handlers, and Attributes

Table of Contents

1. Overview
2. Methods
2.1. Naming methods
2.2. Simple Arguments
2.3. Return Values
3. Events
3.1. Implicit "on" events
3.2. The event tag
3.3. LzEvent and the sendEvent method
4. Event Handlers
4.1. "Onevent" open tag syntax"
4.2. The <handler> tag
4.3. Multiple handlers for the same event
4.4. Handlers that call methods
4.5. Handlers in class definitions
4.6. Overriding handlers
4.7. Comparing ways to send and handle events
4.8. Testing for existence of events in "legacy" code
5. Attributes
5.1. Categories of Attributes
6. The <attribute> tag
6.1. Attributes and Constraints
6.2. Attribute types
6.3. Attribute evaluation time
6.4. Accessing attribute values from JavaScript
6.5. Defining <attribute> setters
6.6. oninit event vs init method
6.7. The keyword this
6.8. Different ways to get and set attributes

1.  Overview

In LZX, the concepts of methods, events, handlers and attributes are related to each other in a braid-like way. In this chapter we'll show you how they weave together.

Attributes, events, methods and handlers are all properties of classes that define how instances of the class behave. Attributes represent the state of the instance. Events are signifiers that communicate with the rest of the application when something about the instance changes. Methods are blocks of Javascript that are executed when the method is invoked by name. Handlers are a special kind of method, each specifically tied to an event.

When an event occurs, any handler associated with that event gets called. Events may have any number of handlers, and handlers can be used to glue events from one instance to methods in another.

Because LZX allows you to add properties to instances, you can add attributes, events, methods and handlers to any instance. (In the terms of other object oriented languages such as Java, effectively LZX allows you to define a class of which there is exactly one instance with the new properties.)

In LZX you use attributes, events, and handlers to describe how your application responds to user input or other changes. In general, and to a first approximation, when anattribute changes value, it can generate an event . (See below for an qualification of when setting an attribute's value does and does not cause and event to be sent.) You can additionally declare events with an <event> tag. Your application's response to that event is determined by the associatedevent handler, which is defined in a <handler> tag.

Methods, events and attributes are the only concepts you need for many kinds of applications. Certain kinds of more subtle event-driven situations require a fourth concept, delegates, which are described in Chapter 30, Delegates.

In the discussion below we'll assume a commonsense understanding of the meaning of <attribute> . Attributes are examined more thoroughly at the end of this chapter.

2. Methods

A <method> is an 'action', expressed in Javascript you can perform on an instance. You can call it from any script.

Methods may be defined inside any <node> or <view> (or classes derived from them). When defining a method, you must specify a value for the nameattribute. The value of the name attribute represents the name by which you will call this method in script. When you call the method, you use its name followed by open and close parentheses -- any arguments to the method would be placed within the parentheses.

You cannot include a <method> within a dataset -- if you do it will be considered data, not an operation to be performed.

In the following example, the myMethod() method is defined in the only node in the example. It is called by the line in the script myNode.myMethod(). (By using the ID, the node can be referenced from anywhere in the program, including this script block).

Example 29.1. Defining a method

<canvas debug="true">
  <debug /> 
  <node id="myNode"> 
    <method name="myMethod">
      Debug.write('Nice day if it do not rain');          
    </method>
  </node> 
  <script>
   myNode.myMethod()
  </script>
</canvas>

2.1. Naming methods

Choose method names carefully. Names should be descriptive, to help you and other readers of your code understand what they do. You need also to keep in mind that when you extend a class, your new class inherits the methods of the class from which it was extended. If you give a new method the name of already existing method, the original method will be overridden. To avoid such "naming collisions" you should consult the LZX Reference, which lists all methods for all Laszlo Foundation Classes (LFC).

2.1.1. Overriding Methods

When you create a class by extending another one, the new class inherits all the methods of the original. You can then give a new definition to any of the inherited methods; this is called overriding. See Chapter 33, Extending Classes for a complete discussion.

2.2. Simple Arguments

The method attribute 'args' is used to pass arguments to a method. The value of the 'args' attribute is a string containing variable names separated by commas. In the following example, two arguments 'a' and 'b' are declared in the args attribute of the method named 'add'. To call this method in script to add the number 1 to the number 2, you would write myNode.add(1, 2);

Example 29.2. Defining a method with arguments

<canvas debug="true">
  <debug y="10"/>
  <node id="myNode">
    <method name="add" args="a,b">
      var sum = a + b;
      Debug.write('a + b = ' + sum);          
    </method>
  </node>
  <script>
     myNode.add(4,9)
   </script>
</canvas> 

2.3.  Return Values

Sometimes when you write a method, you want the result to be returned to the calling script for further processing. In the following example, returnsum() method uses the 'return' keyword. In the script block, this value is used in turn to compute another.

Example 29.3. Returning data from a method

<canvas debug="true">
  <debug y="10"/>
  <node id="myNode">
    <method name="returnsum" args="a,b">
      var sum = 3 + 4;
      Debug.write('a + b = ' + sum);          
      return sum;
    </method>
  </node>
  <script>
      var x = 5;
      var y = myNode.returnsum();
      var z = x + y;
      Debug.write('z equals ' +  z)
  </script>
</canvas> 

3. Events

In LZX, changes in the status of objects are communicated through events. Events can be used to trigger the execution of script. The script to be executed can be contained in an event handler, as explained in this chapter, or in a method that is invoked by a delegate as explained in Chapter 30, Delegates.

Events can be defined either implicitly or explicitly. Implicit events are associated with attributes. Explicit events are declared with the <event> tag.

In addition to the events that you define, there are also events that are built into the LFC but which are not associated with any attribute. Examples includ onclick and onfocus.

Unlike events in similar systems, OpenLaszlo's events are point-to-point, meaning that there is no general broadcast mechanism for events, and events do not trickle up or down the instance hierarchy. Declaring an event that no handler (or delegate) is listening for has no effect. This allows objects to publish many more events than they actually need to create at runtime; this allows you flexibility in prototyping.

3.1. Implicit "on" events

Any defined attribute has an associated event called "on" plus the name of the attribute. Whenever the value of any attribute is updated using the setAttribute(), its associated "on" event is generated. For example, the "height" attribute of a view has an associated "onheight" event that is generated whenever the view's height changes. These events are implicit in the sense that they are built into the system — you don't have to declare them or send them.

Said another way, the default behavior of the setAttribute() method is to set the named property and send the event. For instance, when a view changes its x position, it sends the event onx with the new value for its x property.

In addition to the implicit event associated with each named attribute, LFC classes have a variety of additional events associated with them. For example, the <view> tag has an onclick event that is generated whenever the user clicks the mouse while the cursor is positioned over the view— even though there is no attribute named "click". See the LZX Reference Manual for a complete list of events associated with LFC classes.

3.2. The event tag

In addition to built-in and implicit events, you can explicitly declare events with the <event> tag.

Note that in 3.n and earlier releases, you can send events that have not been explicitly declared. However, this is not a good practice, and applications that rely on this will break with release 4.0. Therefore, it's good to get in the habit of declaring any events that you know you will send by calling sendEvent().

3.2.1. Naming events

As discussed in the section below on handlers, events and their associated handlers must have the same name. Note: event names cannot start with a leading underscore.

3.3. LzEvent and the sendEvent method

When you create an event using the <event>, you are creating an instance of the class called LzEvent. You can then call the sendEvent() on this class to explicitly cause the event to be sent.

Example 29.4. sendEvent

<button name="framitzbutton" onclick="sendframitz()">
     Send framitz event
     <event name="framitz"/>
     <method name="sendframitz">
      framitz.sendEvent()
     </method>
 </button>

You shouldn't send an event for which no handler has been defined. That is to say, it is fine to define an event (using the <event> tag) for which no handler exists. This is often useful to to in prototyping. But don't send it unless you know that there is a handler for it.

Note that there is virtually never any reason to create an event in script by creating a new LzEvent. Likewise, there is virtually never any reason for you to use any method on this event other than sendEvent. Other methods are used internally within the Laszlo Foundation Classes; they are not intended for user code.

4. Event Handlers

An event handler is the code that is executed when an event is recieved. An event can have zero or more handlers.

There are two syntaxes that you can use to define handlers:

  • "onevent" in the node creation tag

  • using the <handler> tag

For convenience, you can use the onevent syntax for short handlers for some kinds of events, such as onclick, as explained below. For readability, however, the < handler> syntax is often better.

4.1. "Onevent" open tag syntax"

The "onevent" syntax is available for attributes that correspond to the CSS event model—onclick, onmouseover, and so forth. For such events, the simplest way to declare an event handler is simply to include it in the definition tag of the node that contains the attribute. This syntax does not work for other events generated by class attributes; for those you must use the < handler> syntax.

In the example below, the event handler for the event generated by the user mouse click changes the views's color from red to blue.

Example 29.5. Event handler defined in opening tag

<canvas height="50">
   <view height="30" width="30" bgcolor="red" onclick="setAttribute('bgcolor', blue)"/>
</canvas>

4.2. The <handler> tag

The <handler> tag defines how an instance responds to an event. A handler can either specify the code to handle an event or it can specify a method that should be invoked to handle the event. Any event can have an unlimited number of handlers associated with it.

4.2.1. Handler tag "name" attribute

The <handler> tag defines code that is executed when an event that has the name of the handler is fired. The name of the handler is the same as the name of the event with which it is associated. For example, the "onclick" event is handled by a handler also named "onclick". Event handlers defined in this way are executed whenever their associated event occurs.

The following example shows how you might handle a button click:

Example 29.6. A simple handler

<canvas height="40">
  <button text="not clicked">
    <handler name="onclick">
      this.setAttribute('text', 'clicked'); 
    </handler>
  </button>
</canvas>  

4.3. Multiple handlers for the same event

An event can have associated with it any number of handlers. When that event is sent, all handlers associated with it are executed.

Example 29.7. Multiple handlers for one event

<canvas debug="true">
  <debug y=" 20"/>
  <button>
   Howdy!
   <handler name="onclick">
      Debug.write("handler one")
   </handler>
     <handler name="onclick">
      Debug.write("handler two")
   </handler>
  <handler name="onclick">
      Debug.write("handler three")
   </handler>
   </button>
</canvas>

4.4. Handlers that call methods

A handler may include javascript code to be executed when the associated event occurs, or it may reference a method that contains the code to be executed. To reference another method, use the method attribute of the <handler> tag.

Example 29.8. Calling a method from a handler

<canvas debug="true">
   <debug y="50"/>
 <simplelayout/>
 <view name="myview" height="30" width="30" bgcolor="blue"/>
 <button>
   Make red the blue box!
  <handler name="onclick" method="redify"/>
  <method name="redify">
     parent.myview.setAttribute("bgcolor", red)
  </method>
  </button>
</canvas>

4.5. Handlers in class definitions

A handler is not overridable in a subclass, so you can add as many handlers as you like to an event and they will all fire.

If you want a subclass to be able to override the handling of an event in a superclass, the superclass would associate a method with the event by:

<handler name="eventName" method="methodName" />

And the subclass would overrride methodName.

Recall that a method is an 'action' you can perform on an instance, and that you can call it from any script. You could call it from a handler. For example, the usage above can be written longhand as:

<handler name="eventName">
  this.methodName();
</handler>

4.6. Overriding handlers

If you want the superclass to define a handler that the subclass can override, you would have the handler point to a method, and in the subclass you just override the method. Let's repeat that to make it clear: when you want the subclass to have a different behavior than the superclass, don't attempt to override the handler in the subclass, and don't create a new handler in the subclass. Instead, have the handler in the superclass call a method, and then in the subclass, override that method. Try this example, which hopefully makes all clear:

Example 29.9. Overriding event-handler method in subclass

<canvas debug="true">
  <class name="base" extends="button">
    <handler name="onclick" method="handleonclick" />
    <method name="handleonclick">
      Debug.write("base click");
    </method>
  </class>

  <class name="subaccumulate" extends="base">
    <handler name="onclick">
      Debug.write("subaccumulate click");
    </handler>
  </class>

  <class name="suboverride" extends="base">
    <method name="handleonclick">
      Debug.write("suboverride click");
    </method>
  </class>

  <subaccumulate>accumulate</subaccumulate>
  <suboverride>override</suboverride>
  <simplelayout />
</canvas>

Here is the same example with instances:

Example 29.10. Overriding event-handling method in an intance

<canvas debug="true">
  <class name="base" extends="button">
    <handler name="onclick" method="handleonclick" />
    <method name="handleonclick">
      Debug.write("base click");
    </method>
  </class>

  <base text="Accumulate">
    <handler name="onclick">
      Debug.write("subaccumulate click");
    </handler>
  </base>

  <base text="Override">
    <method name="handleonclick">
      Debug.write("suboverride click");
    </method>
  </base>

  <simplelayout />
</canvas>

Suppose you want to handle, in one class, an event from some other object? In that case, you would use the reference attribute:

<handler name="onclick" reference="yours">
  Debug.write("Someone is pushing your button");
</handler>

Here's an example:

Example 29.11. Referencing an event in another object

<canvas debug="true">
  <class name="base" extends="button">
    <handler name="onclick" method="handleonclick" />
    <method name="handleonclick">
      Debug.write("base click");
    </method>
  </class>

  <base id="yours" text="Yours">
    <handler name="onclick">
      Debug.write("Your click");
    </handler>
  </base>

  <base text="Mine">
    <method name="handleonclick">
      Debug.write("My click");
    </method>
    <handler name="onclick" reference="yours">
      Debug.write("Someone is pushing your buttons.");
    </handler>
  </base>

  <simplelayout />
</canvas>

Note that Mine handles a click on Yours. Even though the handler is in Mine, it only runs when you click Yours.

4.7. Comparing ways to send and handle events

As has been discussed above, you can cause an event to be sent explicitly, by defining the event and then calling the sendEvent() on it. Or, you can use the setAttribute() method to implicitly define and send an event. A third way of defining events, using delegates, is explained in Chapter 30, Delegates.

<canvas debug="true">
     <debug />

    <class name="myclass_with_handler">
        <event name="myevent"/>
        <handler name="myevent" args="myargs">
            Debug.write(this, 'handling myevent, myargs=', myargs);
        </handler>

        <attribute name="season" type="string" value="spring" />
        <event name="onseason" />
        <handler name="onseason" args="s">
            Debug.write("got season of ", s, ", season is ", season);
        </handler>
    </class>

  <myclass_with_handler id="foo" />


  <button onclick="foo.myevent.sendEvent(12)" text="send the event" />
  <button onclick="foo.setAttribute('season', 'fall')" text="autumn" />

  <!-- Don't do this! Setting an attribute directly with '=' shortcuts around
       the event system. This is not right; use setAttribute instead -->
  <button onclick="foo.season='winter'" text="evil" />

  <simplelayout axis="x" spacing="5" />

</canvas>

4.8. Testing for existence of events in "legacy" code

As is explained in Chapter 30, Delegates, an event doesn't really exist unless and until there is a delegate registered to recieve it. In code that was written before the <event> tag became part of the language, before sending an event it was necessary to test whether the receiving delegate existed; otherwise an error resulted. In order to create your own event, you needed to create an attribute like this:

<attribute name="onsomeevent" value="null"/>

Then, you had to check to see if it was a valid LzEvent before calling sendEvent()() on it. Ways of making that test look like this:

    if ( onsomeevent instanceof LzEvent) {
       onsomeevent.sendEvent();
    }

or like this:

    if (classroot.onactivate) {
       classroot.onactivate.sendEvent();
    }

If you use the <event> and <handler> syntax, you do not have to worry about such tests. (The delegates are handled for you by the runtime.)

5. Attributes

Attributes define properties of instances of classes. For example, consider the <view> element. It has more than forty defined attributes, such as x, y,bgcolor. Some of these attributes are defined on <view>, and others are inherited from <node> from which it derives. These attributes are specified in the schema that defines the LZX tags. Each attribute has a type, for example: number, boolean, and string.

Once an attribute has been defined, you can use it in the open tag for that class. For example, name is a defined attribute of view, so you can write

<view name="charlie"/>

But, for example, framitz is not a defined attribute of<view>, so this would generate an error:


<view framitz="whatnot"/>.

You can define new attributes using the <attribute> tag, for example:

<view name="bob">
   <attribute name="framitz"/>
</view>

Attributes can be an element of a tag or a property of a JavaSctipt class. Attributes that are properties of t Attributes are usually declared and set in tags, but they can also be set and read in script. However, not all attributes can be set in script, similarly not all attributes can be in tags. Attributes are characterized based on this behavior into five categories, as explained below.

An attribute can be declared in a tag header as follows:

Example 29.12. Setting an attribute value in the tag header

<canvas height="20">
  <view width="20" height="20" bgcolor="red"/>
</canvas>

An alternative way to set the attribute is using the <attribute> tag as a child of the tag whose attribute is being set:

Example 29.13. Using the attribute element to set an attribute value

<canvas height="20">
  <view>
    <attribute name="width" type="number" value="20"/>
    <attribute name="height" type="number" value="20"/>
    <attribute name="bgcolor" type="color" value="red"/>
  </view>
</canvas>

This second example is the same as saying <view width="20" height="20" bgcolor="red"/>. The <attribute> tag is useful for writing classes as well as for performing complicated constraints of existing attributes.

In script, the values of most attributes are can be retrieved using dot syntax:

Example 29.14. Using dot syntax to retrieve an attribute value

<canvas height="20">
  <view name="myView" width="20" height="20" bgcolor="red"/>

  <script>
    var myAttributeValue = myView.x;
    // myAttributeValue now has the value 20
  </script>
</canvas>

Attributes can also be read using the getAttribute() method. This is unnecessary most of the time, but can be useful for retrieving the value of an arbitrary attribute, whose name is represented by a string.

Example 29.15. Using getAttribute to retrieve an attribute value

<canvas height="20">
  <view name="myView" width="20" height="20" bgcolor="red"/>

  <script>
    var myAttributeName = "x";
    var myAttributeValue = myView.getAttribute(myAttributeName);
    // myAttributeValue now has the value 20
  </script>
</canvas>

All attributes that are settable in script (see below) can be set using the setAttribute() method:

Example 29.16. Using setAttribute to set an attribute value

<canvas height="20">
  <view width="20" height="20" bgcolor="red"
      oninit="this.setAttribute('width', 50);"/>
</canvas>

5.1. Categories of Attributes

Attributes can be placed into five categories according to how they are set, read, and modified.

  • Attributes (with setter)

  • Attributes (without setter)

  • Event Handler (script may be defined in XML tag)

  • Final Attributes (defined only in XML tag)

  • Read-only Attributes (JavaScript fields)

These are described briefly in the following sections.

5.1.1. Attributes (with setter)

These are built-in attributes which have setters that may be modified at runtime and used in constraint expressions. When setAttribute() is called, the appropriate setter is called automatically. The value of an attribute can be retrieved through script using dot syntax, for example, myView.opacity.

For example:

Example 29.17. Using setAttribute to update a constraint

<canvas height="20">
  <view id="myView" onclick="setAttribute('opacity', 1.5 - this.opacity)" bgcolor="red">
    <text text="${'My opacity is ' + myView.opacity + 'x  Click to change it.'}"/>
  </view>
</canvas>

5.1.2. Attributes (without setter)

Some attributes are usable in a tag, but do not have a predefined setter method. Instead they use the default setter method (setAttribute()) to set their values at run-time. Typically they are custom attributes that have been declared in components using the <attribute> tag. Therefore they can be declared and set in tags too.

Example 29.18. An attribute without a setter method

<canvas height="200" width="600" >
    <window height="50" width="200" title="My Window" onclick="this.setAttribute('title', 'Hello World');"/>
</canvas>

5.1.3. Event Handler Attributes

Event Handler attributes are instructions for what to perform when a particular event happens. They always contain script, and cannot be changed at run-time (i.e. from script). Their values cannot (and do not need to) be retrieved from script.

<canvas debug="true" height="400"> <debug y="200"/> <view width="50" height="50" bgcolor="red" onclick="Debug.write('Hello, World!');" /> </canvas>

5.1.4. Final Attributes

Final attributes are declared and set in the tag, but cannot be changed in using script. Good examples of final attributes are name and id. They can be read from script using dot syntax (e.g. myView.name).

5.1.5. Read Only Attributes (Fields)

Read Only attributes, sometimes called "Fields", are only available through the element's API. Since they are read-only, they cannot be set in a <tag>. Their values can be retrieved using dot syntax (e.g. myView.subviews).

6. The <attribute> tag

The <attribute> tag has two uses.

In a class definition, the <attribute> tag defines attributes that can be set in instances of the class. For example:

Example 29.19. Defining an attribute in a class

<canvas height="36">
  <class name="diamond" width="${this.size}" height="${this.size}" rotation="45">
    <attribute name="size" type="number"/>
  </class>
  <diamond size="25" x="36" bgcolor="red"/>
</canvas>

In an instance, the <attribute> element sets the attribute of the object that it is attached to. In this use, the <attribute> element is equivalent to the use of an attribute in a tag header. For example, the following program uses a tag element to set the width of the view, and an <attribute> tag to set its height.

Example 29.20. Defining an attribute in a view

<canvas height="25">
  <view width="25" bgcolor="red">
    <attribute name="height" value="25"/>
  </view>
</canvas>

In the example above the width is set in the tag header and the height is set using the <attribute> tag. These syntaxes are functionally equivalent for attributes that are defined in the LZX schema—that is, attributes that are part of LZX. As we will show later, using the <attribute> can increase readability.

You can also define new attributes. For these you must use the <attribute> tag.

6.1. Attributes and Constraints

Attributes can be constrained to the value of other attributes. See the Chapter 27, Constraints for details.

6.2. Attribute types

By default, attributes are of the JavaScript expression type, but in some cases the default declaration doesn't give enough instruction to achieve the desired result. In this example, no labels are displayed in the boxes:

Example 29.21. Incorrect: attribute has no effect

<canvas height="300">
    <simplelayout spacing="5"/>
    <class name="box" height="100" width="100" bgcolor="red">
       <attribute name="label" value="Label"/>
       <text text="${parent.label}" />
    </class>

    <box label="Box1"/>
    <box/>
</canvas>

To make sure attributes do what you want them to do, assign a type to your attributes when you declare them:

Example 29.22. Declaring attribute type

<canvas height="300">
    <simplelayout spacing="5"/>
    <class name="box" height="100" width="100" bgcolor="red">
       <attribute name="label" value="Label"  type="string"/>
       <text text="${parent.label}" />
    </class>

    <box label="Box1"/>
    <box/>
</canvas>

6.2.1. Using the <type> option

The type option specifies the type of the attribute's value and affects how a value expression will be parsed:

string

An XML string. To assign the value of an expression that yields a JavaScript String, use value="${expression}".

color

A colorLiteral (see table below). To assign the value of a JavaScript expression, use value="${expression}". The expression must yield a numeric color value.

boolean

"true" or "false"

expression

An ECMAScript expression.

number

A numberLiteral, or an ${expression} which evaluates to a number.

size

A sizeLiteral, or an ${expression} which evaluates to a non-negative number.

text

plain unicode text or html

html

html-encoded text

All other types (boolean, expression, number, size, text) are parsed as expressions of the specified type.

Note that in XML, types are specified in lower case, e.g., string. In JavaScript, types are capitalized, e.g., String . The two types are equivalent in LZX, but you must use the XML name in XML constructs and the JavaScript name in JavaScript constructs.

6.2.2. Summary of color literals

Colors can have the following values:

colorLiteral

A color of the form #hhh, #hhhhhh, rgb(rv, gv, bv) or a CSS color name, where h is a hexadecimal digit, rv, gv, and bv are numbers between 0.0 and 1.0 inclusive, and the CSS color names are from the table below.

black 000000  
green 008000  
silver C0C0C0  
lime 00FF00  
gray 808080  
olive 808000  
white FFFFFF  
yellow FFFF00  
maroon 800000  
navy 000080  
red FF0000  
blue 0000FF  
purple 800080  
teal 008080  
fuchsia FF00FF  
aqua 00FFFF  

6.2.3. JavaScript attribute types

JavaScript types start with a capital letter. These may be one of the native JavaScript types, listed below or an LZX class.

Native JavaScript types

   

Boolean

true or false

String

single or double quotes may be used to specify a sequence of characters (e.g. var s = 'test' or var s = "test"

Number

used to specify simple values (e.g. var n = 4 or var n=4.2). Number type is also commonly used to specify a color, for which it is often convenient to use hexadecimal notation (e.g. var c = 0xFFFFFF for white, or 0x0099BB for turquoise)

Array

an ordered list of elements. The elements may be of any type and need not be of the same type.

Notes on documentation:

   

[LzNode]

An LZX class enclosed in brackets indicates an Array of these types.

dictionary

Also known as a hash, or JavaScript Object, the dictionary type indicates an unordered collection of name-value pairs. For example: {width:100, height:50, title:"my title"}

any

JavaScript APIs will often allow a parameter of any type. This is indicated by the word "any" in the type column.

6.3. Attribute evaluation time

The value of an attribute, whether set in an attribute element or start tag, is evaluated according to the attribute when option. when can be one of the following:

immediately

initializes the attribute to the value of the expression when the enclosing element is defined. The value must be a constant expression and cannot depend on any other objects. immediately is currently the default value for when, but will be deprecated in a future release.

once

initializes the attribute to the value of the expression when the enclosing element is initialized. The expression cannot depend on the value of any other property of the element, nor can it depend on being evaluated in any particular order with respect to any other attributes: use an init method if ordered evaluation is required.

always

updates the attribute any time the value of the expression changes: the attribute is constrained to follow the value of the expression.

The declared evaluation time of an attribute can be overridden when assigning a value by using value="$when{expression}" (or in a start tag by <tag attribute="$when{expression}">), where when is one of the possible when options. If omitted, when defaults to always.

when is a declaration and applies to any setting of the tag, not just the initial one. For example, the width and height attributes of view are declared when="always": any time you set width or height, it creates a constraint, even if you don't say ${expression}. (The compiler is optimized to evaluate constant constraints only once).

${expression} can be used to override the declaration at any site where you are setting the value of an attribute. For example, the title attribute of view is declared when="once", so if you want to have a dynamically updating title, you need to say title=${some.reference} to constrain it to follow some.reference.

Note that an attribute that is not given an initial value will not be created in the element by default: it should either be initialized in the init method using this.attrName=expression or made a required attribute using the required="true" option.

6.4. Accessing attribute values from JavaScript

Attributes can normally be referred to in class methods and expressions by their name except when being initialized as above, in which case they must be referred to using this., in order to create the attribute in the element.

For example:

Example 29.23. attribute for internal flags

<class name="myclass">
  <!-- 'foo' has an initial value of 1 -->
  <attribute name="foo" value="1"/>
  <!-- 'thing' is always four more than 'foo' -->
  <attribute name="thing" value="foo + 4" when="always"/>
  <!-- 'bar' is only declared, initialized below -->
  <attribute name="bar"/>

  <handler name="oninit">
    <!--
      the attribute 'bar' will be created in the instance
      sets its value to true
      is appropriate for internal script flags
    -->
    this.bar= true; 
    <!-- 
      probably a mistake...
      sets the value of attribute 'foo' to 4,
      but does not update the constraint on 'thing'
      use setAttribute, as below
    -->
    this.foo = 4;            
  </handler>

  <method name="dothis">
    if (bar) {
      <!--
        sets the value of 'foo' to 6             
        sends the event 'onfoo'
        (which causes dependent constraints to be evaluated)
        as a result 'thing' will equal 10
      -->
      this.setAttribute("foo", 6);
    }                
  </method>
</class>

6.5.  Defining <attribute> setters

In many cases, specific code operations need to take place when an attribute is set. For example, when the width attribute of a <view> is set, the view needs to update the width of the parent view if clipping is set to false. Though it is very convenient to simply handle the onwidth event to execute the required code, there is no guarantee as to order events will be called. This task is instead accomplished best using the setAttribute() method of <view>, to set the width attribute.

In the following example, the time between clicks is shown on a button. For every click, the current time in milliseconds is stored in the time attribute. The custom setter, setTime(), has been defined for the time attribute.

Normally, when an attribute is set by invoking setAttribute(), setAttribute() handles the tasks of setting the value of the attribute, and sending the onattribute event. You can change this behavior by using the setter attribute on the <attribute> tag. You would then define a method that has the name of the setter.

When defining a custom setter for an attribute, you are responsible for setting the value and sending the onattribute event, as shown in the example below. If the onattribute event isn't sent in the custom defined settter, registered onattribute events will not fire, and constraints tied to the attribute will not be updated when the value of the attribute is changed.

Example 29.24. Defining an explicit setter method

<canvas height="40"> 
  <button text="click me">
    <attribute name="time" setter="setTime(time)"/> 
    <attribute name="ontime" value="null"/> 

    <handler name="onclick"> 
      Debug.write('inited:' + this.isinited); 
      var now = (new Date()).getTime();
      this.setAttribute("time", now); 
    </handler> 

    <method name="setTime" args="t"> 
      if (!this.isinited) {                                          //catch the case where this.time is being 
        this.time = t;                                               //initialized during object instantiation
        return;
      } 

      if (typeof this.time == "undefined" || this.time == null) {    //handle first set of time
        this.setAttribute("text", "first click registered");
      } 
      else {
        var diff = t - this.time;                                    //handle any additional setting of time
        this.setAttribute("text", diff + " milliseconds between clicks");             
      } 
      this.time = t;                                                 //as this is the declared setter for 
                                                                     //this.time, we have to set it

      if (ontime)                                                    //required to update constraints and 
        this.ontime.sendEvent();                                     //fire ontime event 
    </method> 
  </button> 
</canvas> 

6.6. oninit event vs init method

Instances and subclasses of LzNode, including LzView and classes defined using <class> , have both an init() method and an oninit event. Since you do not have control over the order that events are fired, you should put all initialization code in the init() method (and not, for example, in a handler for oninit).

If you declare an init() method in a view or class, you are effectively overriding the init() method in that object's super class. The <view> tag and all its subclasses have important initialization code in the init method, and it is essential that this code is executed when overriding init(). This is accomplished by using the super keyword, which is a reference to the superclass. In the example below, we override the init method in the button class. Notice the call to super.init(), which executes initialization code for the button. The init() method for button subsequently invokes super.init on basebutton, and so on up to the execution of LzNode.init().

Example 29.25. init() method vs oninit event

<canvas height="40">
  <button>
    <method name="init">
      super.init(); //best practice to always call super.init() when declaring 
                    //an init method
      //do some init code here
    </method>
    
    <handler name="oninit">
      //this isn't fired in any particular order 
      //use the init method instead
    </handler>
  </button>
</canvas> 

6.7. The keyword this

When setting variables local to the current class, node, or view, always refer to those variables using the keyword this.

Example 29.26. Using "this" keyword

<canvas height="50">
   <view x=" 10" height="30" width="30" bgcolor="red"
   onclick="this.bringToFront()"/>
   <view  height="30" width="30" bgcolor="blue"
   onclick="this.bringToFront()"/> 
</canvas>

Not using this can result in unpredictable results.

6.8.  Different ways to get and set attributes

There are four ways of getting and setting attributes in LZX, each with benefits and drawbacks:

  • Using setAttribute()() and getAttribute()()

  • Using predefined "setters" for certain attributes, such as onx() and ony()

  • using custom getter and setter methods, as explained above

  • Reading and writing attributes directly—that is, without using a setter or getter method

6.8.1. setAttribute() and getAttribute()

The most reliable way to set and get attributes is by invoking getAttribute() or setAttribute() on the view, class, or node that contains the target attribute. Invoking setAttribute() automatically fires the associated 'on' event for the attribute in question. Having the associated event fire is required for constraints tied to the attribute in question to function correctly.

Performance wise, using getAttribute() and setAttribute() are the slowest way to have access to attributes. The reasons for this slowness is twofold:

  • Method calls are generally considered slow when compared to making no method call at all

  • setAttribute() fires the associated onattribute event, and updates constraints on the target attribute.

6.8.2.  Explicit attribute setter methods

Some attributes of OpenLaszlo Runtime Library classes have explicit getter and setter methods defined for them. The LzView class has several of these such as setX(), setWidth(), etc. Performance wise and functionally, the use of these methods is essentially the same as making calls to setAttribute() or getAttribute(). For consistency, use of these methods is discouraged: Use setAttribute() and getAttribute() instead.

Example 29.27. Using a setter method

<node>
  <attribute name="foo"/>
  
  <method name="doSomething">
    this.setWidth(10); 
    Debug.write('width' + this.getWidth());        
  </method> 
</node>

If you write your own setter or getter method, it will not automatically be invoked by setAttribute() or getAttribute(), thus this practice is not recommended. Instead, the best practice for executing specific code when an attribute is set is to handle the associated onattribute event.

6.8.3.  Getting and setting attributes directly

It is also possible to get and set attributes directly without invoking any method at all, however, this practice is generally discouraged as it may result in unexpected behavior. Since no method call is made, the associated onattribute events will not be fired. Any constraints tied to the attribute also will not be updated. When setting attributes on LFC objects, it is definitely a bad idea to set attributes directly as their explicit setter methods, which contain important processing code, won't be called.

Example 29.28. Setting attributes directly

<node>
  <attribute name="foo"/>
  
  <method name="doSomething">
    this.width = 10;
    Debug.write('width' + this.width);
  </method>
</node>

For these reasons, performance wise, setting directly —for example, setX—is the fastest way to modify an attribute, and may be appropriate in a limited set of situations, though is not considered "safe". Getting attributes directly is safe. Getting attributes directly, and setting attributes using setAttribute(), while seemingly the ideal solution, is inconsistent, and may cause confusion or errors when new developers unfamiliar with LZX are brought on to a project. Consider these choices carefully when setting coding standards for your LZX project.