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. Introduction to 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 an attribute 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 associated event 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 name attribute. 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 height="30" width="100%">
  <text id="message"> 
    <method name="myMethod">
      this.setAttribute('text', 'Nice day if it doesn\'t rain.');         
    </method>
  </text> 
  <script>
    message.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 height="50" width="100%">
  <text id="message">
    <method name="add" args="a,b">
      var sum = a + b;
      this.setAttribute('text', ('a + b = ' + sum));          
    </method>
  </text>
  <script>
    message.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 height="50" width="100%">
  <text id="message" multiline="true">
    <method name="returnsum" args="a,b">
      var sum = 3 + 4;
      this.setAttribute('text', ('a + b = ' + sum));          
      return sum;
    </method>
  </text>
  <script>
    var x = 5;
    var y = message.returnsum();
    var z = x + y;
    message.addText('\nz 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 include 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] 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 received. 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" width="100%">
   <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" width="100%">
  <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 single events

<canvas height="100" width="100%">
   <text y="20" id="message" multiline="true"/>
   <button>
      Howdy!
      <handler name="onclick">
         message.addText("\nhandler one")
      </handler>
      <handler name="onclick">
         message.addText("\nhandler two")
      </handler>
      <handler name="onclick">
         message.addText("\nhandler 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 height="50" width="100%">
  <simplelayout/>
  <view name="myview" height="30" width="30" bgcolor="blue"/>
  <button>
    Make red the blue box!
    <handler name="onclick" method="redify"/>
    <method name="redify" args="v">
      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 an event-handler method in a subclass

<canvas height="200" width="100%">
  <class name="base" extends="button">
    <handler name="onclick" method="handleonclick"/>
    <method name="handleonclick" args="v">
      message.addText("\nbase click");
    </method>
  </class>
  
  <class name="subaccumulate" extends="base">
    <handler name="onclick">
      message.addText("\nsubaccumulate click");
    </handler>
  </class>
  
  <class name="suboverride" extends="base">
    <method name="handleonclick" args="v">
      message.addText("\nsuboverride click");
    </method>
  </class>
  <subaccumulate>accumulate</subaccumulate>
  <suboverride>override</suboverride>
  <text id="message" multiline="true"/>
  <simplelayout/>
</canvas>

Here is the same example with instances:

Example 29.10. Overriding an event-handling method in an instance

<canvas height="200" width="100%">
  <class name="base" extends="button">
    <handler name="onclick" method="handleonclick"/>
    <method name="handleonclick" args="v">
      message.addText("\nbase click");
    </method>
  </class> 
  <base text="Accumulate">
    <handler name="onclick">
      message.addText("\nsubaccumulate click");
    </handler>
  </base> 
  <base text="Override">
    <method name="handleonclick" args="v">
      message.addText("\nsuboverride click");
    </method>
  </base>
  <text id="message" multiline="true"/>
  <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.debug("Someone is pushing your button");
</handler>

Here's an example using <text>:

Example 29.11. Referencing an event in another object

<canvas height="200" width="100%">
  <class name="base" extends="button">
    <handler name="onclick" method="handleonclick"/>
    <method name="handleonclick" args="v">
      Debug.debug("base click");
    </method>
  </class>
  <simplelayout/>
  <base id="yours" text="Yours">
    <handler name="onclick">
      message.addText("\nYour click");
    </handler>
  </base>  
  <base text="Mine">
    <method name="handleonclick" args="v">
      message.addText("\nMy click");
    </method>
    <handler name="onclick" reference="yours">
      message.addText("\nSomeone is pushing your buttons.");
    </handler>
  </base> 
  <text id="message" y="40" multiline="true"/>
</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.

Example 29.12. Different ways to send events

<canvas width="100%" debug="true">  
    <class name="myclass_with_handler">
        <event name="myevent"/>
        <handler name="myevent" args="myargs">
            Debug.debug("%#w handling myevent, myargs = %w", this, myargs);
        </handler>       
        <attribute name="season" type="string" value="spring"/>
        <event name="onseason"/>
        <handler name="onseason" args="s">
            Debug.debug("got season of %w, season is %w", s, season);
        </handler>
    </class>   
    <myclass_with_handler id="foo"/>
    <simplelayout axis="x" spacing="5"/>
    <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"/>
</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 receive 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. Introduction to 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, and 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>

An attribute can be an element of a tag or a property of a JavaScript class. Attributes are declared and set in tags, but they can also be set and read in script. Not all attributes can be set in script, similarly not all attributes can be in tags. Attributes are characterized based on this behavior into four categories, described in Section 5.1, “Categories of Attributes”.

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

Example 29.13. Setting an attribute value in the tag header

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

An alternative (although more verbose) way to set the attribute is using the <attribute> tag as a child of the tag whose attribute is being set:

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

<canvas height="20" width="100%">
  <view>
    <attribute name="width" value="20"/>
    <attribute name="height" 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 can be retrieved using dot syntax:

Example 29.15. Using dot syntax to retrieve an attribute value

<canvas height="40">
  <simplelayout/>
  <view name="myView" width="20" height="20" bgcolor="red"/> 
  <text oninit="this.format('myView.width = %d', myView.width)"/>
</canvas>

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

Example 29.16. Using setAttribute() to set an attribute value

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

5.1. Categories of Attributes

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

  • read/write attributes

  • event-handler attributes

  • initialize-only attributes

  • read-only attributes

These are described briefly in the following sections.

5.1.1. read/write attributes

read/write attributes may be modified at runtime and be the target of a constraint expression. When setAttribute() is called the value of the attribute will be updated, along with any constraints that depend on the attribute. The value of an attribute can be retrieved through script using dot syntax, (for example, myView.opacity).

For example:

Example 29.17. read/write attributes

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

5.1.2. 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 (that is, from script). Their values cannot (and do not need to) be retrieved from script.

Example 29.18. event-handler attributes

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

There is a long-hand version for event-handler attributes, just like normal attributes:

Example 29.19. long-hand version for event-handler attributes

<canvas height="40">
    <text>
        Click me!
        <handler name="onclick">
            this.setAttribute('text', 'Hello World!');
        </handler>
    </text>
</canvas>

5.1.3. initialize-only attributes

initialize-only attributes are declared and set in the tag, but cannot be changed in using script. Good examples of initialize-only attributes are name and id. They can be read from script using dot syntax.

Example 29.20. initialize-only attributes

<canvas height="40">
    <text id="myID">
        Click me!
        <handler name="onclick">
            this.setAttribute('text', this.id);
        </handler>
    </text>
</canvas>

5.1.4. read-only attributes

read-only attributes, sometimes called fields, are only accessible using script. Since they are read-only, they cannot be set in a <tag>. Their values can be retrieved using dot syntax.

Example 29.21. read-only attributes

<canvas height="130" debug="true">
    <debug y="30"/>
    <text id="myID">
        Click me!
        <handler name="onclick">
            Debug.format("canvas.subviews: %w", canvas.subviews);
        </handler>
    </text>
</canvas>

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.22. Defining an attribute in a class

<canvas height="36" width="100%">
  <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>

You can also use an instance to extend a class, just like a new class would (but without defining the class, if you only need one instance). So, just as in a class, the attribute element can be used to define an additional attribute.

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.23. Defining an attribute in a view

<canvas height="25" width="100%">
  <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, the labels are undefined, so undefined is displayed in the boxes:

Example 29.24. Incorrect attribute has no effect

<canvas height="300" width="100%">
    <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.25. Declaring attribute type

<canvas height="300" width="100%">
    <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 attribute

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

Table 29.1. Using the type attribute

Value Definition
string An ECMAScript String in ECMAScript String literal syntax. To assign the value of an expression that yields a ECMAScript String, use value="${expression}".
color A colorLiteral (see Section 6.2.2, “Summary of color literals”). 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 XML content
html XML content

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 defined in lz.colors.

Table 29.2. CSS color names

CSS Name Hex Value
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 JavaScript types, listed below or an LZX class.

Table 29.3. Native JavaScript types

Type Legal Values
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.

Table 29.4. Notes on Documentation

Symbol Notes
[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 wil ofter 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.26. 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.27. Defining an explicit setter method

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

    <handler name="onclick"> 
      Debug.debug('inited: %w', 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.28. init() method vs oninit event

<canvas height="40" width="100%">
  <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.29. Using "this" keyword

<canvas height="50" width="100%">
   <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 the . operator

  • 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()

The only reliable way to set attributes is by invoking 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.

6.8.1.1. Setting attributes directly

The only time you would set an attribute using `.` would be if you were the "owner" of the attribute (it is in a class or instance that you are writing). If the attribute is from a class that you are using or sub-classing, or an instance that is from code that you did not write (built-in, library, etc.) you must use setAttribute() to set the value or you will surely break things.