Table of Contents
This section assumes you're familiar with basic LZX concepts such as views, methods, and attributes. Familiarity with objected-oriented programming (OOP) concepts is helpful, but not required. For a primer on classes in LZX see Chapter 28, Classes.
Inheritance allows you to create custom classes from other predefined classes. The advantage is that much of the logic doesn't have to be rewritten every time you want to create a class that does something similar, but in a slightly different way.
A subclass is a class derived from another class. The class
from which it is derived is referred to as its superclass. A
subclass is said to inherit methods and attributes from its
superclass. Visual elements of a superclass, such as
<view>
, are also inherited by each subclass. In LZX,
you use the extends
attribute to the <class>
tag to create subclasses.
<class name="myclass" extends="mysuperclass"> ... </class>
Any class that you create without using the
extends
attribute is assumed to be a subclass
of <view>
. All methods and properties from
<view>
are inherited by the subclass. These two
declarations are equivalent:
<class name="myclass"/> <class name="myclass" extends="view"/>
You can only extend one class per class declaration, though the inheritance chain can be arbitrarily deep. Multiple inheritance is not supported; that is to say you cannot create a new class that extends more than one existing classes. Methods, handlers, attributes, and views inside a class are inherited down through each level.
Example 33.1. Inheritance chain
<canvas
height
="125
" width
="100%
">
<class
name
="top
">
<attribute
name
="myfoo
" value
="bar
" type
="string
"/>
</class
>
<class
name
="middle
" extends
="top
">
<method
name
="doit
">
message.addText("\nmyfoo is " + this.myfoo);
</method
>
</class
>
<class
name
="bottom
" extends
="middle
">
<button
text
="clickme
" onclick
="parent.doit()
"/>
</class
>
<text
id
="message
" y
="30
" multiline
="true
"/>
<bottom
/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Class definitions can include default values for attributes. For example, a class that extends view can have default
width
and height
attributes:
A subclass can override, that is, provide a different implementation from, a superclass method, but not for a handler.
When a subclass defines a method with the same name as the super class, it overrides or replaces the superclass method. If the superclass method's effect is desired the subclass method must invoke it using a super call. You cannot change the arguments when you override a method: they must match.
When a subclass defines a handler with the same name as the superclass, it adds to the superclass handler. Both will be invoked when the named event is sent.
LZX does not support overloading methods. That means that you cannot have different methods, within one class, that have the same name. The runtime only "cares about" the name of the calling method. You will receive a compilation warning if you define two or more methods with the same name in a class definition.
You can, however, have more than one handler for an event, as described in Chapter 29, Methods, Events, Handlers, and Attributes
You can modify inherited attributes by defining a new value using
the <attribute>
tag or by declaring it as
an attribute in the <class>
declaration itself.
Example 33.2. Inheriting properties
<canvas
height
="100
" width
="100%
">
<!-- create a simplelayout so embedded views are laid out -->
<!-- on top of each other. -->
<class
name
="class1
" layout
="class: simplelayout; spacing: 2
">
<attribute
name
="label
" value
="class1 label
" type
="string
"/>
<text
>from class1
</text
>
<button
text
="${classroot.label}
"/>
</class
>
<!-- overrides class1's label; inherits text and button -->
<class
name
="class2
" extends
="class1
" label
="class2 label
">
<text
>from class2
</text
>
</class
>
<!-- inherit class1's text and button; inherit class2's text -->
<class
name
="class3
" extends
="class2
">
<text
>from class3
</text
>
</class
>
<class3
/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
You can use the super
keyword to invoke a superclass's
method. The super
keyword is useful in instances where you want to
extend the superclass's method without rewriting the same logic. A
method can only use super
to call the method that it
overrides. That is, a subclass's myfunc()
can only call super.myfunc()
, super.myotherfunc()
.
Example 33.3. The super keyword
<canvas
height
="140
" width
="100%
">
<class
name
="foo
">
<method
name
="talk
">
message.addText("\nhello");
</method
>
<button
text
="click
" onclick
="parent.talk()
"/>
</class
>
<class
name
="bar
" extends
="foo
">
<method
name
="talk
">
super.talk();
message.addText("\ngoodbye");
</method
>
</class
>
<text
id
="message
" y
="30
" multiline
="true
"/>
<bar
/>
</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 order to override the behavior of an event handler, you would have the handler call a method, and then override the method
in the subclass or instance. For example, let's say that you wanted to write a handler for the onclick
event that you could override in an instance. In your class definition, you would use this syntax:
<handler name="onclick" method="handleClick">
Then in the instance you would define handleClick()
.
Remember, you can call the original click code in the new method by using the super method:
<method name="handleClick"> super.handleClick(); Debug.debug('new click'); </method>
Use the <setter>
tag to declare or override
attribute setter methods. Note that this functionality is new, and you may have to
change your code written for OpenLaszlo 4.1 and earlier. For example, this 4.1.x code:
<method name="setY" args="y" > super.setY( Math.max(parent.arrowbuttonheight-1 , Math.min( y , parent.height - (parent.thumbheight + parent.arrowbuttonheight)))); </method>
Would need to be changed to:
<setter name="y" args="y" > super.setAttribute('y', Math.max(parent.arrowbuttonheight-1 , Math.min( y , parent.height - (parent.thumbheight + parent.arrowbuttonheight)))); </setter>
Here's another example. If you had this code in OpenLaszlo 4.1 or earlier:
<text x="${Math.round((parent.width-this.width)/2)}" font="vera_super_bold" fgcolor="0x4d4d4d" datapath="@username" resize="true"> <method name="setText"> if (typeof(this.datapath.data) != 'undefined') { super.setText(this.datapath.data + "'s information"); } </method> </text>
You would change it to:
<text x="${Math.round((parent.width-this.width)/2)}" font="vera_super_bold" fgcolor="0x4d4d4d" datapath="@username" resize="true"> <setter name="text"> if (typeof(this.datapath.data) != 'undefined') { super.setAttribute('text', this.datapath.data + "'s information"); } </setter> </text>
LFC setter methods like setWidth()
, setHeight()
,
and setBGColor()
are now deprecated. They continue to
work but if you use them, a warning message is displayed in the debugging console.
Some LFC components mimic text objects. For example, <edittext>
is similar to <inputtext>
but it does not extend a text object.
This is what the text attribute used to look like in <edittext>
:
<attribute name="text" type="text" setter="setText(text)"/> ... <method name="setText" args="t"> this.text = t; if (this._initcomplete) { this.setValue(t, true); this.field.setAttribute('text', this.value); if (this['ontext']) this.ontext.sendEvent(); } else { this._initialtext = t; } </method>
As you can see, this allows setting the text object via setAttribute('text',...)
and setText()
. To make this code run a little faster (by removing a call to setText()
from the setter), and follow how the LFC does it, this was rewritten to use the <setter>
tag:
<attribute name="text" type="text"/> ... <method name="setText" args="t"> Debug.warn("edittext.setText is deprecated. Use setAttribute instead"); this.setAttribute('text', t); </method> <setter name="text" args="t"> this.text = t; if (this._initcomplete) { this.setValue(t, true); this.field.setAttribute('text', this.value); if (this['ontext']) this.ontext.sendEvent(); } else { this._initialtext = t; } </setter>
The classroot
property is a convenient short-hand that
refers to the root node of a class instance. It's often used by a
deeply nested view or method that needs access to something near the
root of the class. Though you can equivalently use
parent
, parent.parent
,
parent.parent.parent
, etc. (depending on how deep the
view that contains the reference is nested), using classroot is
usually more readable.
Example 33.4. Classroot
<canvas
height
="200
" width
="100%
">
<class
name
="deep
">
<attribute
name
="mytext
" value
="hello, world
" type
="string
"/>
<view
bgcolor
="red
" width
="250
" height
="150
">
<view
bgcolor
="green
" width
="75%
" height
="75%
">
<button
text
="clickme
" width
="75%
" height
="75%
">
<!-- classroot is a convenient way to access mytext -->
<handler
name
="onclick
">
message.addText("\nclassroot.mytext: " + classroot.mytext);
message.addText("\nparent.parent.parent.mytext: " +
parent.parent.parent.mytext);
</handler
>
</button
>
</view
>
</view
>
<text
id
="message
" y
="140
" multiline
="true
"/>
</class
>
<deep
/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Be careful when using classroot
from the root of the
class. If there is no surrounding class, classroot
will
be undefined. Use the this
keyword in code attached to
the root of the class. On the other hand, if an instance of a class
appears inside another class, the classroot
for the
instance will be the root of the surrounding class. Use this feature
as a short-cut to refer to the root of outer classes.
Example 33.5. Referring to outer class's root using classroot
<canvas
debug
="true
" height
="200
" width
="100%
">
<debug
height
="175
"/>
<class
name
="foo
">
<method
name
="doit
">
Debug.debug("foo: this is [%w]", this);
Debug.debug("foo: classroot is [%w]", classroot);
Debug.debug("foo: classroot.classroot is [%w]", classroot.classroot);
</method
>
</class
>
<!-- boo contains a foo -->
<class
name
="boo
">
<foo
name
="myfoo
"/>
</class
>
<!-- goo contains a boo -->
<class
name
="goo
">
<boo
name
="myboo
"/>
<handler
name
="oninit
">
myboo.myfoo.doit();
Debug.debug("-----");
Debug.debug("goo: this is [%w]", this);
// error will be displayed -- there is no classroot
Debug.debug("goo: classroot is [%w]", classroot);
</handler
>
</class
>
<!-- Make an instance of goo -->
<goo
name
="mygoo
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
At the risk of belaboring the topic, here's one more example that demonstrates that top level instances of a class have no defined classroot. Notice that even though "bar" is a child of "foo", it does not have a defined classroot, because it is a toplevel instance of the bar class.
Example 33.6. Classroot and top level instance
<canvas
debug
="true
" width
="100%
">
<class
name
="myclass
" height
="40
" bgcolor
="blue
">
<attribute
name
="button_label
" type
="text
" value
="button
"/>
<handler
name
="onclick
">
this.handleclick()
</handler
>
<method
name
="handleclick
">
Debug.debug("this = %w, and classroot is %w", this, classroot);
</method
>
<button
name
="b1
" height
="30
" text
="${parent.button_label}
" onclick
="Debug.debug('classroooot of this button is: %#w', classroot); parent.handleclick()
"/>
</class
>
<class
name
="another_class
" extends
="myclass
"/>
<!-- foo and bar views are top-level instances, so their classroots are null.
== The buttons are children of the foo and bar views, respectively, so ==
== their classroots are defined. -->
<myclass
id
="foo
">
<another_class
id
="bar
" bgcolor
="red
" button_label
="another button
" y
="25
">
<method
name
="handleclick
">
Debug.debug("this = %w, and classroot is %w", this, classroot);
</method
>
</another_class
>
</myclass
>
</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 page describes a basic usage of <mixin>
. For a more advanced usage, see the Reference.
<mixin>
is like <class>
but rather than creating something you can instantiate, <mixin>
creates a template that can be
mixed in (added to) more than one other class. A
<mixin>
is like an interface, but it is allowed to have
state and implementation.
<mixin>
is an advanced feature. To add a
<mixin>
to a class you say:
<class name="icecream" ... /> <class name="walnuts" ... /> <mixin name="toppings"> <attribute name="sauce" type="color" value="lavender" /> <attribute name="whippedcream" type="boolean" value="true" /> <attribute name="cherry" type="boolean" value="true" /> ... </mixin> <class name="sundae" extends="icecream" with="toppings /> <sundae name="chocfulla" flavor="honeydew" sauce="lemonchiffon"> <walnuts ground="true" /> </sundae>
The <text>
and <inputtext>
tags are unique among the built in
classes in that they can hold textual content:
<canvas height="50" layout="y"> <inputtext>plain text</inputtext> <text><i>styled</i> text</text> </canvas>
The text may contain character text, as well as certain XHTML markup tags. The inputtext tag may contain character text.
Classes that extend the text and inputtext classes may contain XHTML and plain text, respectively. The text content is available as the text attribute of such a class.
Example 33.7. Extending text classes
<canvas
width
="100%
" height
="50
" layout
="axis: y
">
<class
name
="mytext
" extends
="text
"/>
<class
name
="myinputtext
" extends
="inputtext
"/>
<myinputtext
>plain text
</myinputtext
>
<mytext
><i
>styled
</i
> text
</mytext
>
</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 user-defined class can also handle text content by defining an
attribute named text
with a type of
text
(for plain text) or html
(for XHTML
text), like <attribute name="text"
type="text">
. There is another text type available called
string
which allows you set a text string as an
attribute, but does not allow text content.
Consider these two classes, where the first is defined using
type="string"
and the second with type="text"
:
<class name="stringText"> <attribute name="text" type="string"/> </class> <class name="textText"> <attribute name="text" type="text"/> </class>
Both classes can be used with a text attribute:
<stringText text="some text"/> <textText text="some text"/>
Only textText
can be used with text content. The
use of stringText below is invalid, and will produce a compilation
warning.
<stringText>some text</stringText> <!-- This is invalid --> <textText>some text</textText> <!-- This is valid -->
Using the html
type declares that the class can accept
html text content, or set through the text
attribute. The text may contain XHTML tags such as <b>
and
<a>
, as in the following program:
Example 33.8. Text type: HTML
<canvas
height
="50
" width
="100%
">
<class
name
="htmlText
">
<attribute
name
="text
" type
="html
"/>
<text
resize
="true
" text
="${parent.text}
"/>
</class
>
<simplelayout
/>
<htmlText
>
<b
>bold
</b
> text declared here with
<a
href
="http://www.openlaszlo.org
"><i
>anchor
</i
></a
>
</htmlText
>
<htmlText
text
="<b>bold</b> text set here
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Note that XHTML markup within a class that is declared with
type="text"
, instead of type="html"
, is
invalid:
<textText>some text</textText> <!-- valid --> <textText>some <i>fancy</i> text</textText> <!-- invalid --> <htmlText>some text</htmlText> <!-- valid --> <htmlText>some <i>fancy</i> text</htmlText> <!-- valid -->
Any text content with a class that is declared without a text
attribute whose type is text
or html
is
invalid:
<class name="noText"/> <noText>some text</noText> <!-- invalid --> <noText>some <i>fancy</i> text</noText> <!-- invalid -->
The attribute name must be text
. It is an error to
use text
and html
as the type of any other
attribute:
<class name="invalidClass"> <attribute name="label" type="text"/> <!-- invalid --> </class>
Classes and views enclosed in classes inherit their font and font
properties (such as fontstyle
and
fontsize
) from their superclass. Also, any
class instance will inherit its font from its enclosing view. A font
can be overridden at any point in a view hierarchy and any subview
from there on will inherit that font.
Example 33.9. Inheriting fonts
<canvas
height
="50
" width
="100%
">
<font
src
="helmetr.ttf
" name
="Helvetica
"/>
<font
src
="helmetb.ttf
" name
="Helvetica
" style
="bold
"/>
<class
name
="foo
">
<!-- view overrides inherited fontstyle to plain -->
<view
fontstyle
="plain
" bgcolor
="yellow
">
<!-- text overrides inherited fontsize to 12 -->
<text
fontsize
="12
">hello
</text
>
</view
>
</class
>
<class
name
="bar
" extends
="foo
" layout
="axis: y
">
<text
>goodbye
</text
>
</class
>
<bar
font
="Helvetica
" fontstyle
="bold
" fontsize
="12
"/>
</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 general, instantiation of objects happen using tags. For
instance, assuming <class name="myclass">
is
declared, you can create an instance of that class by writing
<myclass/>
. However, there may be times when you
will need to instantiate an object using script. The script
instantiation syntax for classes looks like:
var myobject = new lz.myclass(parent
,attributes
,children
,instcall
)
where:
parent
is where your object will be
placed in the node hierarchy. If it doesn't matter, then you
can pass null
. If you are creating a subclass of
view
and the parent is null
, the
canvas
will be the parent of this object.
attributes
is a hash of attribute values
that get passed into the object. For example, if you wanted to
instantiate a new view
with a different bgcolor,
width, and height, you could pass in {bgcolor: 0xff0000, width:
50, height: 50}
.
children
is the array of child views this object
encapsulates. The OpenLaszlo Runtime instantiator is responsible for passing in the
children of this object
based on the LZX hierarchy. You will
generally set this to null.
instcall
is a Boolean value that determines
when this object will be instantiated. If false, the instantiation
of this object will be immediate, otherwise, its instantiation
will be synchronized with the rest of the view system. See Appendix A, Understanding Instantiation.
All parameters are optional. Not setting any of these arguments (for example, new lz.myclass()
)
is equivalent to new lz.myclass(null, null, null, 0)
.
The following example shows you how to instantiate a new object through script and add it to another view.
Example 33.10. Script instantiation
<canvas
height
="120
" width
="100%
">
<class
name
="mybox
">
<view
bgcolor
="${parent.bgcolor}
" width
="50
" height
="50
"/>
</class
>
<view
name
="redbox
" bgcolor
="red
" width
="100
" height
="100
"/>
<!-- Create new lz.mybox with cyan bgcolor and place it in canvas.redbox. -->
<button
x
="110
" text
="add cyan
" onclick
="if (canvas.redbox['cyan'] == null) new lz.mybox(canvas.redbox, { name: 'cyan', bgcolor: 0x00ffff })
"/>
<!-- Remove cyan view from redbox. -->
<button
x
="110
" y
="30
" text
="remove cyan
" onclick
="if (canvas.redbox['cyan'] != null) canvas.redbox.cyan.destroy()
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Views that you create procedurally are not the same as "clones" created by data replication. In fact, data replication overrides procedurally created views. For example:
Declare a view.
Add subviews to it (procedurally), and alter its properties.
Set a datapath on the view (from step 1) that would make it replicate.
Changes made in step 2 will be ignored after replication.
Be aware that names for tag classes are not the same as their
JavaScript counterpart; rather, the JavaScript name is lz[tagname]
. For example, you can create and instantiate a class someclass
like this:
// create the class <class name="someclass"> //class definition </class> // make one new lz.someclass; //or new lz['someclass'];
You can instantiate a variable class like this:
var theclass = 'someclass'; // make one new lz[theclass];
An approach you can take to writing classes is just to declare the class tags and have an instance of that class on the canvas. This will give you the framework to see what it looks like while you're building up your class, for example:
<canvas> <class name="myclass"> ... </class> <myclass/> </canvas>
Optionally, you can sketch out a class by writing a view first and then
transforming it into a class. The drawbacks of this approach are that you can't use the
classroot
keyword and attributes can't be declared.
Top-level views inherited from a superclass are placed in the top-level of a subclass. The inherited views from the superclass will be placed first in order. This can be verified by examining the subviews array.
Example 33.11. Inheriting views
<canvas
debug
="true
" width
="100%
">
<debug
y
="215
"/>
<class
name
="one
">
<view
name
="r
" bgcolor
="red
" width
="200
" height
="200
"/>
</class
>
<class
name
="two
" extends
="one
">
<view
name
="g
" bgcolor
="green
" width
="100
" height
="100
"/>
</class
>
<class
name
="three
" extends
="two
">
<view
name
="t
" bgcolor
="teal
" width
="50
" height
="50
"/>
<view
name
="y
" bgcolor
="yellow
" width
="25
" height
="25
"/>
</class
>
<three
id
="mysubclass
" oninit
="Debug.debug('subviews: %w', this.subviews)
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Views declared in an instance of a class will be placed in the
top-level of the class unless otherwise declared with the
defaultplacement
attribute. Those views will be
placed with a later order in the subviews array. The
defaultplacement
attribute tells a class where
declared views should be placed in the hierarchy of the class and is
explained in more detail in the next section.
Example 33.12. Inherited view order
<canvas
debug
="true
" height
="250
" width
="100%
">
<class
name
="foo
">
<view
name
="red
" bgcolor
="red
" width
="100
" height
="100
"/>
</class
>
<foo
name
="myfoo
" oninit
="Debug.debug('subviews: %w', this.subviews)
">
<view
name
="yellow
" bgcolor
="yellow
" width
="50
" height
="50
"/>
</foo
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Notice how view yellow
follows view
red
in its subviews array. If a
<simplelayout>
is placed in view
myfoo
, they will be displayed in order of
r
followed by y
.
Example 33.13. Inherited view order with simplelayout
<canvas
debug
="true
" height
="250
" width
="100%
">
<debug
x
="75
" y
="115
"/>
<class
name
="foo
">
<view
name
="red
" bgcolor
="red
" width
="100
" height
="100
"/>
</class
>
<foo
name
="myfoo
" oninit
="Debug.debug('subviews: %w', this.subviews)
">
<simplelayout
/>
<view
name
="yellow
" bgcolor
="yellow
" width
="50
" height
="50
"/>
</foo
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
The internal structure of a class is generally not visible to its hierarchical children. By default, instances which appear inside a class are made children of the top level instance of the class. This is generally not desirable for container classes. For example:
Example 33.14. Undesired placement
<canvas
height
="50
" width
="100%
">
<class
name
="myframe
" extends
="view
">
<attribute
name
="bgcolor
" value
="red
"/>
<view
x
="5
" y
="5
" width
="${parent.width-10}
" height
="${parent.height-10}
" bgcolor
="#FFFFCC
"/>
</class
>
<!-- make an instance of myframe with text inside it-->
<myframe
width
="220
" height
="20
">
<text
>This is some text
</text
>
</myframe
>
</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 behavior can be changed using the
defaultplacement
attribute or the
determinePlacement()
method. Using
defaultplacement
is simple — this is a
class attribute that identifies by name the subview where a child
should be attached. The child will be attached to the first subview
with a matching name. If none is found, the child is placed as a
subview in the top-level node of the class, as would have happened if
no defaultplacement
had been specified.
Be aware that the defaultplacement
attribute
should always be declared in an attribute with
type="string"
.
Example 33.15. Placing a child in the desired subview
<canvas
height
="50
" width
="100%
">
<class
name
="myframe
" extends
="view
">
<attribute
name
="bgcolor
" value
="red
"/>
<!-- child views of class instances will be placed in the first view
called insideview -->
<attribute
name
="defaultplacement
" value
="insideview
" type
="string
"/>
<view
x
="5
" y
="5
" width
="${parent.width-10}
" name
="insideview
" height
="${parent.height-10}
" bgcolor
="#FFFFCC
"/>
</class
>
<!-- make an instance of myframe with text inside it-->
<myframe
width
="220
" height
="50
">
<text
>This is some text
</text
>
</myframe
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Elements declared in a class are not considered for placement, but children in subclasses or class instances will be.
Example 33.16. Defaultplacement
<canvas
height
="160
" width
="100%
">
<class
name
="myframe
" extends
="view
">
<attribute
name
="bgcolor
" value
="red
"/>
<attribute
name
="defaultplacement
" value
="'insideview'
"/>
<view
x
="5
" y
="5
" width
="${parent.width-10}
" name
="insideview
" height
="${parent.height-10}
" bgcolor
="#FFFFCC
"/>
<!-- this view is not affected by defaultplacement -->
<!-- because it's declared in the class. -->
<view
x
="5
" y
="${parent.height}
" name
="anotherview
" width
="${parent.width-10}
" height
="10
" bgcolor
="blue
"/>
</class
>
<class
name
="subframe
" extends
="myframe
">
<!-- the layout and text will be placed in insideview of myframe -->
<simplelayout
axis
="y
"/>
<text
bgcolor
="teal
">subframe text
</text
>
</class
>
<myframe
width
="220
" height
="50
">
<!-- this will be placed in insideview -->
<text
>This is some text
</text
>
</myframe
>
<subframe
width
="220
" height
="50
" y
="70
">
<text
bgcolor
="green
">More subframe text
</text
>
</subframe
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
A layout declared as an attribute will be considered for
placement. This is often the desired behavior because it makes it easy
for subclasses and class instances to modify the layout for views
inside the default placement. To override this behavior, a
layout
attribute can be set with a non-existing
placement
(e.g., placement: null
). The
placement
attribute tells an element's
container where it should go within the container's internal
hierarchy. If the container has a
defaultplacement
, the placement
value has precedence. Alternatively, you can make sure that a layout
isn't handled by defaultplacement by declaring it as a tag element in
the class.
Example 33.17. Layout placement
<canvas
width
="100%
">
<!-- the layout attribute will be placed in the red view -->
<class
name
="myplacement
" defaultplacement
="red
" layout
="axis: x; spacing: 5
">
<!-- this layout element applies to views inside of class -->
<simplelayout
spacing
="10
"/>
<view
name
="red
" bgcolor
="red
" width
="150
" height
="150
"/>
<view
name
="yellow
" bgcolor
="yellow
" width
="150
" height
="150
"/>
</class
>
<myplacement
>
<!-- placement overrides defaultplacement -->
<view
name
="blue
" bgcolor
="blue
" width
="50
" height
="50
" placement
="yellow
"/>
<!-- green and teal will be placed in red -->
<view
name
="green
" width
="50
" height
="50
" bgcolor
="green
"/>
<view
name
="teal
" width
="50
" height
="50
" bgcolor
="teal
"/>
</myplacement
>
</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 child placed using defaultplacement
or
placement
will often have two parent types. The
parent
is the reference to the node that was passed as
the child's ancestor in the constructor, e.g., new
childClass(parent, args)
. If the child was created by declaring
it in a tag, the parent will be its lexical parent. (Its lexical parent
is the tag that encloses it.)
The immediateparent
refers to the node where the
child is actually placed at runtime. Its value will be the same as
parent
if no value is assigned to the placement
attribute.
Example 33.18. Parent vs. Imediateparent
<canvas
debug
="true
" height
="200
" width
="100%
">
<debug
x
="155
"/>
<class
name
="container
" defaultplacement
="red
">
<view
name
="red
" bgcolor
="red
" width
="150
" height
="150
"/>
</class
>
<!-- yellow's parent is top and its immediateparent -->
<!-- is red, since that's where it's actually placed. -->
<container
name
="thetop
">
<view
name
="yellow
" bgcolor
="yellow
" width
="50
" height
="50
">
<handler
name
="oninit
">
Debug.debug("parent: %w", this.parent);
Debug.debug('immediateparent: %w', this.immediateparent);
</handler
>
</view
>
</container
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
There may be instances where a class needs a reference to the default placement node. A good trick is to search the subnodes of the class until it's found.
Example 33.19. Obtaining reference to the defaultplacment node
<canvas
debug
="true
" height
="200
" width
="100%
">
<debug
x
="155
"/>
<class
name
="container
" defaultplacement
="red
">
<attribute
name
="contentview
" value
="null
" type
="expression
"/>
<method
name
="init
">
super.init();
// get a reference to the content node
if ( this.contentview == null ) {
if ( this.defaultplacement != null ){
this.contentview = this.searchSubnodes( "name" , this.defaultplacement );
} else {
this.contentview = this;
}
}
Debug.debug("content view %w", this.contentview);
</method
>
<view
name
="green
" bgcolor
="green
" width
="100
" height
="100
">
<view
name
="yellow
" bgcolor
="yellow
" width
="50%
" height
="50%
">
<view
name
="red
" bgcolor
="red
" width
="50%
" height
="50%
"/>
</view
>
</view
>
</class
>
<container
name
="thetop
"/>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
A node calls its determinePlacement()
method to
determine the immediateparent
of a child. This method
will only be called for subnodes which have a placement attribute, or
for all subnodes if this node has a non-null
defaultplacement
. The
placement
attribute of a subnode overrides a
parent's defaultplacement
. This method looks
for a subnode with the name given in the placement
parameter, and returns that node. If no such named node exists, it
returns this
. The code essentially looks like:
<!-- subnode: the child node to place --> <!-- placement: the node to place the subnode --> <!-- args: init args of the child subnode --> <method name="determinePlacement" args="subnode, placement, args"> // ignore placement if set if ( args.ignoreplacement ){ return this; } if ( placement == null ){ var p = null; } else { var p = this.searchSubnodes( "name" , placement ); } return p == null ? this : p; </method>
A subclass might implement this method to cause the
placement
parameter to have a different behavior or
additional effects. For instance, a subnode could have
parent
and immediateparent
be the
same.
Example 33.20. Overriding determineplacement
<canvas
debug
="true
" height
="200
" width
="100%
">
<debug
x
="155
"/>
<class
name
="container
" defaultplacement
="red
">
<!-- setting subnode's parent to be the same as immediateparent -->
<method
name
="determinePlacement
" args
="subnode, place, args
">
var p = super.determinePlacement(subnode, place, args);
subnode.parent = p;
return p;
</method
>
<view
name
="blue
" bgcolor
="blue
" width
="100
" height
="100
">
<view
name
="red
" bgcolor
="red
" width
="150
" height
="150
"/>
</view
>
</class
>
<container
name
="thetop
">
<view
name
="yellow
" bgcolor
="yellow
" width
="50
" height
="50
">
<handler
name
="oninit
">
Debug.debug("parent: %w", this.parent);
Debug.debug("immediateparent: %w", this.immediateparent);
</handler
>
</view
>
</container
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
When writing complex classes, a deep understanding of how classes are constructed and initialized is essential. Several steps are involved before a class instance is fully initialized. Describing this process is out of the scope of this chapter, but important methods and events that are involved, and the sequence in which they are invoked, will be discussed here. See Appendix A, Understanding Instantiation for a more in-depth discussion.
The construct()
method is called as early as
possible in constructing the view system. It is invoked before any arguments have been applied. This is the method to override
in lieu of writing a
class constructor for your LZX class. If you do override the construct method, be sure to invoke,
the superclass method, or results will be extremely
unpredictable. Among other things, the construct method is the method where the class being constructed places itself in its
container by calling its parent's
determinePlacement()
method. If the superclass
construct method isn't called, the class instance may not be placed
correctly.
Alternatively, you can use the onconstruct
event,
which is sent out by the instantiator after construct()
has been called. The onconstruct
happens right at the end of
the instantiation process, but before any subnodes have been created
or references resolved.
Keep in mind that construction happens top-down (parent to subviews), whereas initialization happens bottom-up (subviews to ancestor).
Example 33.21. Construction and initialization
<canvas
debug
="true
" height
="180
" width
="100%
">
<debug
height
="160
"/>
<class
name
="container
">
<!-- Don't forget to call super.construct(parent,args)!! -->
<method
name
="construct
" args
="parent,args
">
Debug.debug("container construct %w %w", parent, args);
super.construct(parent, args);
</method
>
<!-- The onconstruct event -->
<handler
name
="onconstruct
" args
="v
">
Debug.debug("container onconstruct %w", v);
</handler
>
<method
name
="init
">
Debug.debug("container init");
</method
>
<handler
name
="oninit
">
Debug.debug("container oninit");
</handler
>
</class
>
<container
>
<view
name
="outside
" oninit
="Debug.debug('outside oninit')
">
<view
name
="inside
" oninit
="Debug.debug('inside oninit')
"/>
</view
>
</container
>
</canvas
>
<!-- * X_LZ_COPYRIGHT_BEGIN ***************************************************
* Copyright 2007, 2008 Laszlo Systems, Inc. All Rights Reserved. *
* Use is subject to license terms. *
* X_LZ_COPYRIGHT_END ****************************************************** -->
Following instantiation (i.e., after the onconstruct
is sent) and if there are child nodes, the
createChildren()
method is called. This method takes an
array of child objects as a parameter. Each child object has three
properties:
Example 33.22. createChildren()
<canvas
debug
="true
" height
="180
" width
="100%
">
<debug
height
="160
"/>
<class
name
="container
">
<handler
name
="onconstruct
" args
="v
">
Debug.format("container onconstruct %w", v);
</handler
>
<method
name
="createChildren
" args
="c
">
Debug.debug("container createChildren", c);
Debug.debug(" c[0].name: %w", c[0].name);
Debug.debug(" c[0].attrs: %w", c[0].attrs);
Debug.debug(" c[0].children: %w", c[0].children);
super.createChildren(c);
</method
>
</class
>
<container
>
<view
name
="outside
">
<view
name
="inside
"/>
</view
>
</container
>
</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 summary, you can expect the basic timing order of method and event calls to look like:
classroot
a short-hand that refers to the root node of the instance of a class.
extends
the keyword used in a class declaration to create a subclass.
the concept of classes automatically containing the variables and methods defined in their superclass.
the action that creates an instance of a class or object.
using one identifier to refer to multiple functions.
to provide a different method implementation in the subclass from its superclass.
the class that derives from another class.
super
keyword operator that allows a subclass to invoke it's superclass's method.
the class that a subclass derives its attributes and methods from.
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.