Table of Contents
LZX is an object-oriented, tag-based language that uses XML and JavaScript syntax to create the presentation layer of rich Internet applications. Typically these applications are compiled by the OpenLaszlo compiler. They may be deployed as standalone files, or they may be served by the OpenLaszlo Server. The specification of the language includes both a set of XML tags and a set of JavaScript APIs.
The LZX language was designed to use familiar syntaxes and naming conventions so that experienced web developers would find it easy to learn and incorporate into their programming environments. At the same time LZX introduces new concepts and capabilities that make possible more fluid and responsive user interfaces to web applications than are possible with any other technology.
A running LZX program takes place on a visual object called a canvas, which is basically a
bit of screen real estate. On the canvas, autonomous boxes called views interact. These views
may be nested logically and visually, and have programmable attributes, including
size
, position
, background color
, and
so forth. Views may be used to contain resources, such as, for example, an image or a video, and
may also be dynamically bound to any set of XML-formatted data. The attributes of any view can
be set to be a function of the attributes of any other view or views, and virtually any
attribute of a view can be animated — that is, set to vary over time.
The LZX view system is similar to other view systems in many ways, but its implementations of data binding, attribute-constraints, and animation distinguish it from other UI technologies.
LZX programs typically contain both declarative and procedural structures, and the language follows many naming conventions from CSS (Cascading Style Sheets). Programs written in LZX thus appear similar, on casual inspection, to DHTML applications with embedded JavaScript. LZX programs are conceptually different, however, from typical DHTML/JavaScript applications that are interpreted and rendered, or "executed," by the web browser. LZX programs, in contrast, are compiled on the server and downloaded as byte code for a target rendering engine.
In the current implementation of the OpenLaszlo platform, LZX programs are compiled in the OpenLaszlo Server and downloaded either as Flash movies (.swf files) to be executed in the Flash Player plugged into an Internet browser, or compiled to DHTML which is downloaded to be executed by the browser's JavaScript engine. It's important to understand that the Flash Player is used only as an execution/rendering engine for the generated byte code: there's nothing inherent in LZX that marries it to Flash. In particular, LZX does not employ or rely upon the Flash object model.
Similarly, because LZX programs are compiled by the OpenLaszlo Server the use of JavaScript in LZX programs is subtly different from its use in traditional web applications in which JavaScript is used to do things like communicate with the browser or generate HTML pages. Those functions are basically irrelevant in LZX applications. Thus, although the language design is rather traditional, the programming paradigm is fundamentally new.
This chapter summarizes the traditional and innovative aspects of LZX. It's an overview, not a tutorial; after reading it you will be better able to decide how to go about learning the language. Depending on your background and experience, you may determine that you need to go learn some more about XML or object-oriented programming before addressing LZX. On the other hand, if you find these concepts accessible, you may want to jump right in and begin coding, in which case we suggest starting with the tutorial: Chapter 5, OpenLaszlo Basics.
For discussion of OpenLaszlo runtime and deployment modes, see the Preface.
In LZX, XML tags are used to create JavaScript objects, and JavaScript is used inside of LZX programs to manipulate objects created by tags. In most cases, anything that can be done with a tag can be accomplished in JavaScript, and vice versa. However, this equivalence is not universally true, and moreover one technique is virtually always vastly superior to another in any situation. Learning LZX basically comes down to learning the tags and APIs; mastering the language requires developing a subtle understanding of how the procedural and declarative approaches differ, and learning how and when to use each.
LZX strictly adheres to XML and JavaScript syntax
The following sections offer a brief refresher on the two kinds of LZX syntax. See below for a discussion of how JavaScript and XML syntaxes play together in typical LZX programs.
XML, the eXtensible Markup Language, is a W3C standard for encoding data. You will need a general familiarity with XML concepts in order to write programs in the LZX language for two reasons: in the first place, most of the functionality of LZX is implemented in an XML tag set. LZX programs themselves are valid XML documents; LZX programs that are not valid XML simply will not compile. Secondly, LZX programs only operate on data encapsulated in XML. See Section 3, “XML Characters in Script” for a description of how to use XML in script.
If you understand how tags and attributes are represented in XML, are comfortable with the concepts of roots and nodes, and know how nesting works you probably know enough to get started with LZX. For more information and links to any number of online books and tutorials about XML, visit the W3C web site.
Another distinction between XML and JavaScript is that in XML the type names are
lowercase ("string", "number"), while in JavaScript they're initial-capped, for example
String
and Number
. The XML type names are used in
<attribute name="foo" type="string"/>
; they're lowercase
for compatibility with the XML Schema Description datatypes.
If you have experience with HTML but not XML, you'll find many similarities. Here's
some notes on how XML differs from the HTML, for people who are only familiar with HTML.
These apply to all XML; they may particularly catch people up with respect to text markup,
which uses tags (<p>
,
<i>
, <br>
) with the same
names and meanings as HTML tags:
Case matters. <b>
is different from
<B>
. (<b>
exists
in LZX as a tag for marking up bold text in a <text>
element. <B>
does not exist.)
Attribute values must be quoted using "
or
'
. <view width=100>
is invalid
XML; use <view width='100'>
or <view
width="100">
instead.
Empty elements must be closed. <br>
is valid
HTML; in XML use <br></br>
or
<br/>
instead.
OpenLaszlo applications can be written with a namespace:
<canvas xmlns="http://www.laszlosystems.com/2003/05/lzx">...</canvas>
or without:
<canvas>...</canvas>
If there is no namespace, the compiler defaults it to the LZX namespace
(http://www.laszlosystems.com/2003/05/lzx
).
Note | |
---|---|
A namespace has the same syntax as an URL, but it is not an URL; namespaces are really just hierarchical unique id's. In the above examples, the namespace doesn't point to anything in particular on the Laszlo Systems website. For more on namespaces, see the W3C specification. |
JavaScript is a language originally written at Netscape by Brendan Eich for incorporation in the Netscape 2.0 browser. It was instantly successful and widely adopted in other browsers, and to preserve its emerging value as a standard, the European Computer Manufacturer's Association (ECMA) codified the language as ECMAScript and now maintains control of its evolution. Although there may be subtle differences between any implementation of JavaScript and the language formally specified by the standards body, in colloquial usage the terms JavaScript and ECMAScript are often used interchangeably. While perhaps a little more accurate to say that "ECMAScript" refers to the pure language while "JavaScript" means both the language and the associated libraries that are available on most browsers, in this book we follow general usage and use the terms loosely, trusting that it will be clear from the context what we're talking about. The term "script" refers to any (procedural) code written in JavaScript.
LZX incorporates a partial implementation of the ECMA-262 Edition 3 specification. See Section 2, “Differences between ECMA-262 and LZX” for more information.
Depending on your background, you may find certain aspects of LZX familiar or foreign. For example, if you have experience with Java but not JavaScript, you will need to be aware of key differences in the languages.
JavaScript's approach to object-oriented programming lacks the rigor of Java's. There are no packages or interfaces, for example, nor is it possible to finalize classes. Finally, the behavior of local and global variables in JavaScript is sometimes surprising to Java programmers.
Conversely, if you are an experienced JavaScript programmer you may have to "unlearn" certain assumptions, in particular with regard to the availability of certain libraries and functions. For example, while the Math RegExp librarys are present in LZX, literal RegExp for regular expressions is not. Moreover, LZX has a more complete object-oriented programming model than does simple JavaScript. That is, LZX has classes, inheritance, and mixins.
If you have experience with neither Java nor JavaScript you may want to first work through a JavaScript tutorial like the ones available at the MDN Doc Center before delving too deeply into LZX.
LZX employs the "dot" (period) syntax to indicate relationships between objects and their members. Consider the expression
something.other
When read as JavaScript, something
refers to an object, and
other
refers to a property of that object, where a property might be,
say, a method. Now consider the following LZX code fragment:
<view name="beatles"> <view name="george"/> </view>
in this case it may sometimes be convenient to refer to the interior view, "george" as
beatles.george
in which case george
is a "child" of beatles
.
As will be discussed further below, LZX affords various ways to define attributes of,
and methods on, objects or classes. For example, the following code samples (which create a
view named myview
and set its background color to red) are equivalent:
JavaScript:
myview = new lz.view;
myview.setAttribute ("bgcolor", "red");
XML tag:
<view name="myview" bgcolor="red"/>
and in both cases the background color of myview
could be accessed by
subsequent code as myview.bgcolor
. The "dot" convention thus provides a
convenient way of referring to objects regardless of how they came into existence — that is,
whether by declarative tag or procedural code.
OpenLaszlo is entirely case-sensitive. This means that whenever you use a variable it must be in the case in which it was defined.
LZX has a behavior that is more Java-like than JavaScript-like. 'implicit this' is a term we use to describe the behavior of free references in LZX methods and handlers. In LZX classes, the object bound to `this` is implicitly 'in scope' in all methods and handlers, as it is in Java (this is _not_ the case in JavaScript). We added this feature to LZX because we felt it led to more intuitive and compact code.
What this means is that in any method or handler in a class you can refer to the class attributes by name directly, without the prefix `this.`. (Hence the nickname 'implicit this'.) A concrete example:
<class name="foo"> <attribute name="attr" value="42" /> <method name="implicitAttrValue"> return attr; </method> <method name="explicitAttrValue"> return this.attr; </method> </class>
The two methods will return the same value.
You should not rely on "implicit this" on the left-hand side (LHS) of an assignment expression. Doing so is dangerous because unless the property already exists in `this`, you will write a global. Consider:
<class name="bar"> <attribute name="attr" /> <method name="implicitSetAttrValue"> attr = 7; </method> <method name="explicitSetAttrValue"> this.attr = 7; </method> </class>
Because attr
is not initialized, the implicit method may not find
attr
in the instance and will set the global variable
attr
instead. You should always explicitly use
this
on the left-hand side of any assignment. The compiler can help you
find errors like this: If you compile your application with lzc -
DwarnGlobalAssignments
, the compiler will print a warning for every global
assignment that your program makes. If you intend to make a global assignment, you can
silence the warning by explicitly using `global.attr =`. For a complete description see
Chapter 49, Understanding Compilation.
`with` is a JavaScript primitive that can be used to establish scope. Free variables in the body of a `with` will be looked up first in the object that is the argument (and then in whatever the enclosing scope is).
An XML schema defines the LZX tag set and can be used to configure an editor. The DTD is also available for the curious, although there is no need for you to be aware of it for programming purposes.
The LZX schema is used by the OpenLaszlo Compiler to ensure that LZX programs are
formally correct. For example, the schema specifies what attributes can be included in an
opening <view>
tag. If your program contains a
<view>
tag that includes an attribute not defined by the
schema, it will compile with a warning.
LZX allows you to define your own tags. User defined tags do not get incorporated into the schema that is available to your editor, although they are used in the internal schema that the compiler uses to test that program's validity.
LZX incorporates the standard object-oriented programming concepts of inheritance,
encapsulation, and polymorphism. In general, a tag in an OpenLaszlo program corresponds to an
object that is an instance of the class of that name. For example, the
<view>
tag corresponds to an lz.view
object.
To a first approximation, then, LZX can be described as a rule-based declarative language for manipulating visual (JavaScript) objects called views, where rules are expressed as constraints on the values of the attributes of those objects.
The following paragraphs summarize some of the key object-oriented aspects of the LZX language. These ideas are examined at greater length elsewhere in this Guide, particularly in Chapter 33, Extending Classes.
An object is a data type that contains named pieces of data. Depending on context, a
named datum might be called a property or an
attribute of that object. For example, each view
object has attributes, such as height, width, horizontal position, vertical position, and so
forth. Values are generally assigned to the attributes of objects when the objects are
created; attributes that you do not specifically set are assigned default values.
You can create new kinds of LZX objects by using the
<class>
tag. Each new class you create must be given a name
and the name of a class that it's "extending." The newly created objects inherit all the
properties of the class you extended, plus any additional properties you may define. For
example consider the trivial case
<class name="myview" extends="view"/>
In this case you have defined a new kind of object called a myview
that has all the properties of a view
. Appendix A, Understanding Instantiation discusses in depth when and how objects are
defined in your code and built by the compiler at runtime.
In LZX, the word "attribute" has two related but subtly different meanings, one syntactical and one semantic. In the XML, syntactical, sense, an attribute is a named value associated with an XML element and specified in that element's opening tag. Thus in the XML tag
<boss demeanor="friendly"/>
demeanor
is an attribute of the tag boss
. This
meaning of "attribute" applies whenever the context is XML structure. Note that the value
assigned to an attribute is enclosed in double quotation marks.
Because LZX tags correspond to JavaScript classes, "attribute" takes on the additional semantic weight of property of a JavaScript object. Thus the LZX tag
<view height="20" width="30"/>
causes the creation of a view object with the specified values for the attributes
height
and width
.
The <attribute>
tag can be used to set JavaScript
attributes of objects. For example,
<view name="myview"> <attribute name="height" value="20"/> <attribute name="width" value="30"/> </view>
is equivalent, in LZX, to the earlier one-line version. Thus height
is an attribute of the view, in the semantic sense, even though it is not in the XML sense
of being contained in the opening tag. height
is also an attribute, in
the XML sense, of the first <attribute>
tag. Its value can
be referenced by script as myview.height
.
You also use the <attribute>
tag to define new
attributes for classes you create. For example
<class name="froboz" extends="view"> <attribute name="whatnot" value="17"/> </class>
Defines a new kind of view object, froboz
, that has the attributes
that it inherited from view, plus the new attribute named whatnot
.
We have seen that attributes can be set, that is, assigned values, in LZX tags. It is
also possible to set attribute values in script using the setAttribute()
setAttribute()
method. Additionally, values of attributes can be read, or
"gotten" in script (but not in tags) using the .
operator.
Assume the existence of a view named johnny
. This view may have been
created by a tag or in script; how the view came into existence does not matter.
The JavaScript code to set the height of this view to 100 pixels would be:
johnny.setAttribute("height", 100);
and to read the value the height would be
johnny.height;
Every time that an attribute is set, that is, every time the value of an attribute
changes, an object called an event
is generated. The next section
discusses what events are, and how they work in LZX programs.
Events are the mechanism by which objects communicate with each other when something changes. For example, an event might be generated when a mouse button is clicked, or when data arrives from a server, or when a view has been constructed.
In LZX programs, events are not broadcast, but rather they are communicated in a
point-to-point fashion using delegates
, which are basically function
pointers that are referenced when events happen. This implementation increases flexibility
and reduces the overhead of using events. However, for the purposes of the discussion in
this chapter we're going to ignore delegates and speak of events in a slightly less rigorous
manner, saying, for example when such-and-such an event occurs, thus
and such happens, leaving aside for now an explanation of
how it happens.
Views have defined events, as listed on the entry for
<view>
in the LZX Reference Manual. Many of these events that deal with
user input, such as onblur
, onclick
,
onkeydown
, will be familiar to JavaScript programmers. Other events,
such as onheight
and onopacity
, pertain to views'
visible attributes. Finally, the oninit
and
onconstruct
events are related to the creation of the instances of view
objects. Similarly, other system-defined LZX objects such as Datasets (see below) have
events associated with them.
Events and attributes often work as pairs, and in fact, the default behavior of the
setAttribute()
method is to set the named property and send the event
called "onx
" + property. For instance, when a view changes its
x
(horizontal) position, it sends the event onx
with
the new value for its x
property. This means that in addition to
system-defined events, there exists an event for each attribute you define.
When an event happens, control is transferred to its associated event-handler (if one is defined). Events can be sent with a single argument, which usually conveys information about the property that changed.
Elsewhere in this Guide we discuss how events are implemented in LZX, and how the event architecture bears upon program design. In particular, Chapter 30, Delegates discusses the relationship between events and delegates.
In LZX, a method is a JavaScript function associated with a particular object.
Functions are invoked using the ()
operator. Thus,
<view name="dog"> <method name="bark"> <!-- some JavaScript code --> </method> </view>
Defines a function that is executed when invoked by name, as in
dog.bark();
In JavaScript, the this
keyword is used to refer to the object
through which the function was invoked.
A handler is like a method. But whereas a method is invoked by its name, a handler must be associated with a particular event. The handler script will be executed when the referenced view receives an event with this name. For example,
<view> <handler name="onclick"> <!-- some JavaScript code --> </handler> </view>
defines a function that is executed when the view is clicked on. Consider
<view> <method name="bark"> <!-- some JavaScript code --> </method> <handler name="onclick"> this.bark() </handler > </view>
When the view is clicked on, the onclick
event causes the execution
of the handler()
which in turn invokes the method named
bark()
. Event handlers are often identified in an opening tag, as in
<view onclick="clickHandler()"> <method name="clickHandler"> <!-- some JavaScript code --> </method> </view>
There are three general categories of methods and handler:
"On init" methods that are invoked when their parent object is created;
handlers that are invoked when their parent object receives a specified event;
named methods that are explicitly invoked by other methods.
Note that you can define a method using conventional JavaScript syntax, but in LZX the
preferred way to declare a method is with the <method>
tag.
Also note that in LZX, unlike many other object-oriented systems, you can override a method in an instance of an object. This topic is covered in Chapter 33, Extending Classes.
In LZX, a constraint is an attribute whose value is a function of other attribute values. The syntax for coding a constraint is
$when
{expression
}
where:
$
is the token indicating a constraint
is an optional compiler directive: when
immediately
,
once
, or always
.
$always{
can be abbreviated
to expression
}${
expression
}
{
and }
are tokens delimiting the expression to
be evaluated
is a JavaScript expression
expression
As we have seen above, whenever the value of an attribute changes, its
on
event is generated. Because a constraint is an attribute whose value is
dependent upon the values of one or more other attribute(s), the value of the constraint is
recalculated whenever it receives the on
event for the attributes on which
it depends.
Consider
<view name="someView" width="${someAttribute + someOtherAttribute}" />
The value of someView.width
is recomputed whenever an
onsomeAttribute
or onsomeOtherAttribute
event
occurred.
So for example
<view name="beatles" width="${this.paul.width + 28}"> <view name="paul" onclick="clickhandler()" > <!-- clickhandler method here to increase paul's width based on user clicking mouse --> </view> </view>
The width of beatles
will increase or decrease as a function of
paul
's width; the expression this.paul.width + 28
is a
constraint.
When the user clicks on the paul
view, the clickhandler will adjust the
size of the paul
view. This change will be reported to the
${this.paul.width + 28}
constraint, which will then adjust the
beatles
view to the width of the paul
view plus an
additional 28 pixels. All of these steps are invisible to the user because they occur
instantly.
This, of course is a trivial example, but it serves to make the point that in declaring the structure of your objects in LZX you also declare the rules by which they will relate to each other. Constraints are a fundamental concept in LZX programming, and learning to "think in LZX" is a mostly a matter of learning to properly model your system's behavior in terms of the constraints on its constituent parts. Chapter 27, Constraints covers constraints in depth.
An LZX application is expressed as a hierarchy of objects, usually visual objects, all of which are contained in a single object called the Canvas. Recall that LZX programs are XML documents, the Canvas is the root element. The simplest LZX program is thus:
<canvas/>
This program compiles and executes, but has no output. As the simplest visual object is the View, a minimal LZX program would look something like:
<canvas> <view> <text> Hello World!</text> </view> </canvas>
This code clearly defines a hierarchy of three objects. We can make their visual relationship more visible by giving the canvas and view sizes and background colors:
In this simple case, the lexical hierarchy in the code corresponds to the visual
hierarchy in the canvas. In fact, a <text>
object is an
instance of a class derived from <view>
. The typical LZX
program repeats this pattern on a larger scale: the canvas contains views which contain
other views, and so forth. Classes are used to replicate view groupings; components such as
buttons, windows, input fields and sliders are examples of classes built from views.
LZX affords a variety of ways to simplify the relationships among views. For example, there are several categories of layouts that handle the "housekeeping" of placing views in relationship to each other. These are described in Chapter 17, Layout and Design.
However, the relationship between the textual hierarchy in the code and the visual
hierarchy on the canvas is not always as neat as in the example above. In particular, LZX's
powerful data binding semantics make it possible for a single
<view>
tag in the text to cause the creation of an
arbitrary number of instances of view
objects. In such cases it becomes
very important to have a precise way of talking about complex relationships among objects.
Chapter 26, Views covers this topic in depth.
In LZX the concepts of local and global namespaces, or scopes, are basically the same as in JavaScript. Having said that, it should be pointed out that JavaScript follows rules that are sometimes surprising to Java Programmers.
In JavaScript, all variables are global unless they are preceded by the keyword
var
.
Thus
a = 7; // defines a global variable a
and
var a = 7 // defines a local variable a
This syntax means, for example, that an assignment in a method definition can set an instance of a global variable:
for (a = 0; a <n; a++);
Creates a global variable named a
, or changes the value of this
variable if it already exists. What the programmer meant to write was
for (var a = 0; a <n; a++);
In LZX, the name
attribute is local and the id
attribute is global. Thus
<canvas> <view name="john" id="grandfather"> <view name="john" id="father"> <view name="john" id="son"/> </view> </view> </canvas>
is a valid name scheme. The innermost view can be referenced by
canvas.john.john.john
or simply son
.
As will be discussed below, functions created using the
<script>
tag can be accessed from anywhere in the program.
LZX is designed to make it easy to write data-driven applications in which the values of data sources define the appearance and actions of the program. It does this through tags and APIs that allow you to get access to data over http, manipulate XML data in memory, and, significantly, bind the data hierarchy to the view hierarchy.
The following paragraphs summarize these key features of the LZX data-handling architecture. Chapter 36, Data, XML, and XPath discusses this subject in depth.
LZX programs manipulate XML-formatted data, which may be
embedded in the program text,
read in from a source when the program is compiled, or
read in from a source when the program is running.
XML sources are stored as objects called datasets. The
<dataset>
tag has attributes that allow you to, for
example, control caching on the client and server, include or exclude http headers, queue
requests, and so forth. Objects created by <dataset>
are
called lz.dataset
s. Methods on lz.dataset
s allow you
to, for example, get and set query strings, parameters and so forth.
LZX employs datapointers, which are objects that represent pointers to nodes in
datasets, to locate and manipulate content. Datapointers support a subset of XPath, which is
a W3C standard specification for identifying paths of an XML document, or in the case of
LZX, datasets. XPath uses a notation similar to the UNIX file-system to refer to nodes
within a dataset. Datapointers can be repositioned using both procedural calls such as
selectNext()
and by running an XPath request using
setXPath()
.
Because it incorporates sophisticated pattern matching, XPath notation is extremely
concise and powerful. A single XPath expression can represent an arbitrarily large number of
XML elements. Using methods such as addNode()
, setNodeName()
setXpath()
, and selectParent()
, you can build and
manipulate XML structures.
LZX provides a unique way of merging any arbitrarily shaped data hierarchy with any display hierarchy; this capability is called data binding. It is implemented in such a way that the data context of a child in the display hierarchy is implicitly the data context of its parent. Moreover, it is possible to instruct the system to create an arbitrary bit of view hierarchy to represent each element in a set of selected data.
The way this is done is by binding views to datapointers. A datapath
is a special case of datapointer that explicitly marries the data hierarchy to the view
hierarchy, as in, for example:
<view name="bob" datapath="testdata:/*">
where testdata
refers to a dataset defined earlier in the program.
If this sounds a little abstract, well, it is. Therefore we'll keep the discussion short here and defer longer explanations to Chapter 37, Data Access and Binding; you may also want to examine some of the examples on http://openlaszlo.org/showcase to get a feel for what can be done when applications are truly data-driven.
The key thing to understand is that while other languages and technologies have implemented merge algorithms that may appear similar on the surface, LZX's data binding is novel in the creation of program objects that retain a live connection with the entities of the data source.
As mentioned earlier, virtually all nontrivial LZX programs contain both (XML) tags and script. Tags are used declaratively, that is, to define objects and their attributes. Script is used procedurally, that is, to explicitly define a series of steps. Although the two kinds of code are liberally intermixed within LZX — for example, script can appear within tags — each syntax locally maintains its integrity.
So for example, within declarative LZX code, comments are denoted
<!-- XML comment -->
while within JavaScript, comments are denoted
// JavaScript comment
Thus LZX is similar to an alloy of two metals that do not chemically combine.
Because the declarative and procedural portions of a program can be so intertwined, it can be a little tricky, at first, to recognize them within a program. However, once you gain a little experience and begin to grasp the underlying logic of LZX you will find that you hardly notice the alternating syntaxes. The following paragraphs explain how and why to employ the two "flavors" of LZX. Consult the documentation for your IDE or text editor to learn how to use the LZX DTD or schema to give visual cues that indicate what portions of the program are in each syntax.
The preferred normalized form of class names is lz.[tagname]
where
[tagname] is the name of a tag. So for example a if you created a class called "bob":
<class name="bob"/>
then from the point of view of JavaScript, this would be an lz.bob
object.
In earlier versions of LZX (before OpenLaszlo 4), there was an asymmetric mapping
between tag and class names, often of the form LzFoo <—>
<foo>
, as in the correspondence between, say the class name
lz.view
and the tag name <view>
.
(Notice in lz.view
the mixed case, and the absence of the period between
lz and the tag name.) Also there was a distinction between LFC classes and user-created
classes. The new lz.foo
form is consistent across LFC classes and
user-created classes. The old forms will still work, and they appear throughout this
documentation and in example code. As of OpenLaszlo 5.0, however, the old forms will be
deprecated; it would be a good practice to adopt the new form in your code.
Let's start by making a distinction between what is syntactically allowable and what is meaningful.
Remember that all LZX programs are well-formed XML files. That means that all portions of the program, including embedded JavaScript, must conform to XML rules. Therefore where JavaScript uses characters that are meaningful to XML, such as the left angle bracket <, you must make sure that those characters don't confuse the XML parser. You can do that in either of two ways:
by explicitly escaping delimiter characters with an
entity reference. (For example, the entity reference for the
left angle bracket is <
).
by using the XML CDATA
construct to define a block of character
data.
This is the sum total of rules for making sure that XML does not trip on JavaScript.
Although the admixture of two different sets of language rules in one language does create opportunities for confusion, it's fairly easy to recognize how LZX programs are structured, and what kind of code goes where. There are only a few contexts in which script code can appear in LZX programs. After you learn to recognize these contexts you are unlikely to be confused about what syntax applies. JavaScript is used:
between an opening and closing <script>
and
</script>
tag;
between an opening and closing <method>
and
</method>
tag;
between an opening and closing <handler>
and
</handler>
tag;
with the double-quoted right hand value of an assignment statement within certain
tags, such as oninit="
.
script expression
"
As we said earlier, most things that you can do in LZX can be done either with XML tags or JavaScript APIs, and mastering LZX requires developing a subtle understanding of how and when to use each. You will find, in general, that tags are best for computations that can be done at compile-time — such as laying out the canvas — and script is best for run-time things, such as responding to user input. But in order for you to make any use of that information you need to understand what is done at compile time and what is done at run time, and much of that is under your control, and dependent on the problem you're trying to solve.
In other words, there is no simple set of unambiguous rules that tell you when to use tags versus when to use script. But there are, however, design patterns common to all well-made LZX programs.
Remember, LZX is primarily a language for manipulating visual objects called views. So the question of when to use tags versus script is usually asked in the context of the creation of views and manipulation of their attributes. Script can be used for other things, such as global functions, but in those instances the need to write procedural code (i.e., script) is usually clear-cut. The finesse part has to do with manipulating views and their attributes.
For example, a simple two-word constraint might express a relationship between views that would require thirty lines of code to express. Most of the time the constraint is the better programming solution. But not always.
Although there are no absolutes, there are some general principles that define best practice in LZX development:
Use tags when that is the only option.
Use JavaScript when that is the only option.
If something can be done with either tags or script, use tags (unless there is a good reason not to).
Each of these principles is described briefly below.
There are certain tags that perform functions that cannot be done using script. For
example, the root node (and enclosing tag) of every LZX program is
<canvas>
. Every LZX program begins with
<canvas>
and ends with
</canvas>
; there is no alternative structure using
script. Similarly there are no script equivalents for
<method>
, <attribute>
,
<resource>
, and several other tags. Moreover, within
certain tag definitions there are certain attributes that can only be set in the tag.
There are several JavaScript APIs that perform functions that cannot be done using
tags. For example, lz.event
and similar APIs perform operations that
cannot be done using tags. Similarly, there are certain attributes of objects that can
only be referenced by script, even for objects that were created with tags. For example,
consider
<view name="franklin">
There is an attribute, franklin.subviews,
that can be accessed by
script; it is not possible to set or access that attribute in a tag.
In the large number of cases where it is possible to do something using either tags or views, it is generally better to use tags. For example you can create a new view called "sam" using tags
<view name="sam">
or script
sam = new lz.view();
When you use the tag syntax you can quite naturally create hierarchies of nested subviews, define attributes as constraints, and lay out your code in a way that helps you conceptualize the placement of views on the canvas. Achieving any of these results in pure JavaScript would be a colossal pain and negate much of the benefit of the language. Learning to think in LZX means learning to think in terms of views that act nearly autonomously according to the constraints you establish at their creation.
Sometimes it's better to write procedural code instead of declarative code. This may become necessary, for example, to achieve optimal performance: multiply-constrained systems can sometimes become CPU bound. Other times procedural code may make your program's behavior easier to understand: complex rule-based view systems sometimes become inscrutable.
LZX programs are compiled by the OpenLaszlo Compiler, downloaded as byte code or DHTML, and executed on the client. In writing your program you can make trade-offs between compilation performance, download and startup up time, and runtime performance.
As in other scripting languages such as Perl and Python, LZX programs execute in approximately linear order. That is to say, if you were to write
<view name="outside"> <view name="inside"/> </view>
Then inside
would be built before outside
. However
in many cases you may wish to control the order in which objects are built, or initialized.
LZX gives you fine grained control over initialization and instantiation of views.
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.