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 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.
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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.
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 received. 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
" width
="100%
">
<view
height
="30
" width
="30
" bgcolor
="red
" onclick
="setAttribute('bgcolor', 'blue')
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007-2009 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
" width
="100%
">
<button
text
="not clicked
">
<handler
name
="onclick
">
this.setAttribute('text', 'clicked');
</handler
>
</button
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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 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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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.
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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.)
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007 - 2009 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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.
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007 - 2009 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 - 2009 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 - 2009 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 - 2009 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 - 2009 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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, 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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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="${ .
|
color
|
A colorLiteral (see Section 6.2.2, “Summary of color literals”). To assign the value of a JavaScript expression, use
value="${ . 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.
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 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 |
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 |
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.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>
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.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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
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
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Not using this
can result in unpredictable results.
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
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.
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.
Copyright © 2002-2010 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.