Table of Contents
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.
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 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>
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).
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.
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>
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>
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.
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.
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()
.
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.
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:
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.
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>
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.
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>
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>
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>
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>
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.
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>
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.)
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>
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.
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>
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>
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>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.
Attributes can be constrained to the value of other attributes. See the Chapter 27, Constraints for details.
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>
The type
option specifies the type of the attribute's value and affects how a value expression will be parsed:
An XML string. To assign the value of an expression that yields a JavaScript String
, use value="${
.
expression
}"
A colorLiteral (see table below). To assign the value of a JavaScript expression,
use value="${
. The expression must yield a numeric color value.
expression
}"
"true" or "false"
An ECMAScript expression.
A numberLiteral, or an ${expression
} which evaluates to a number.
A sizeLiteral, or an ${expression
} which evaluates to a non-negative number.
plain unicode text or 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.
Colors can have the following values:
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 |
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. |
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="$
(or in a start tag by when
{expression
}"<
), where tag
attribute
="$when
{expression
}">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 ${
. (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 expression
}title
attribute of view
is declared when="once"
, so if you want to have a dynamically updating title, you need to say title=${
to constrain it to follow some.reference
}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.
or made a required attribute using the attrName
=expression
required="true"
option.
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>
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 on
event isn't sent in the
custom defined settter, registered attribute
on
events will not fire, and constraints
tied to the attribute will not be updated when the value of the attribute is changed.
attribute
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>
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>
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.
There are four ways of getting and setting attributes in LZX, each with benefits and drawbacks:
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:
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.
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.
Copyright © 2002-2007 Laszlo Systems, Inc. All Rights Reserved. Unauthorized use, duplication or distribution is strictly prohibited. This is the proprietary information of Laszlo Systems, Inc. Use is subject to license terms.