Live Elements uses a declarative syntax to create components. There are a few variations of this syntax, depending on context.
The common way to create a component is by using braces after a component type:
component A{}
const a1 = A{} // creates a new object of type A
The javascript equivalent for the above, which also works (but shouldn't be used), would be:
component A{}
const a1 = new A()
BaseElement.complete(a1)
Components that have a constructor that takes a set of arguments can be created by using the dot (.
) after the constructor, and following with a list of arguments in paranthesis, just before the braces:
component A{
constructor(arg1:any, arg2:any){
super()
this{}
console.log("Created with args: " + arg1 + "," + arg2)
}
}
const a1 = A.(1, 2){} // Created with args: 1,2
Inside the braces, we can add members like properties, functions, events, and some of the functionality similar to when declaring a component:
component A{
number x: 10
number y: 20
}
const a1 = A{
x: 11 // assign property
number z: 33 // add new property
}
console.log(a1.x) // 11
console.log(a1.y) // 20
console.log(a1.z) // 33
The code below shows an example of each of the possible declarations:
component A{}
let a = A{
number x: 10 // property declaration
y: 10 // property assignment
fn toString() string { return ""; } // function
id: a // scope identifier
event triggered()
on triggered: () => {} // listener
B{} // default child 0
C{} // default child 1
}
The following sections will go through each declaration in detail.
The expression below declares a property named x
with a value of 10
:
component A{}
const a = A{
number x: 10
}
console.log(a.x) // 10
The assignmet is optional:
component A{}
const a = A{
number x
}
console.log(a.x) // undefined
Bindings and binding expressions work the same as they would in a component declaration:
component A{}
const a = A{
number x: 10
number y: 20
number z: this.x + this.y // simple binding expression
number t: {
if ( this.x === 20 )
return true
return false
} // complex binding expression
}
a.x = 20
console.log(a.z) // 40
console.log(a.t) // true
Properties that are part of the object can be assigned directly:
component A{
number x: 10
}
const a = A{
x: 30
}
console.log(b.x) // 30
Each component when created can have an id
assigned, which can then be used to reference that component from other components:
component A{
id: a
number x: 10
number x1: B{
id: b
number y: a.x // a is accessible from here
}
number x2: C{ number z: b.y } // b is accessible from here
}
See also component declaration ids.
Methods can be added using the fn
keyword. Method parameter types are required, and return type is optional:
component A{}
const a = A{
fn sum(a:number, b:number) number{
return a + b
}
fn toString() string{
return "A{}"
}
}
console.log(a.toString()) // A{}
console.log(a.sum(2, 3)) // 5
Events are declared using the event
keyword, and, similar to functions, can have arguments. Events are triggered using the emit()
function:
component A{
event event1()
event event2(x:number, y:number)
fn triggerEvent1(){
this.event1.emit()
}
fn triggerEvent2(){
this.event2.emit(10, 20)
}
}
Listeners are preceeded by the on
keyword. They assign a function to be executed when the event was triggered:
component A{
event event1(x:number, y:number)
on event1: () => {
console.log("Event 1 triggered")
}
}
Objects of a component that enable the default children property can nest other objects. See also component default children:
component A{
default children
}
const a = A{
B{} // child 0
C{} // child 1
}
console.log(a.children.length) // 2
Components that declare constructors with arguments can be created using the dot notation followed by the arguments in paranthesis and braces:
component A{
constructor(arg:number){
super()
this{}
console.log("Created component with: " + arg)
}
}
const a1 = A.(1){} // Created component with: 1
The example below shows how to create 2 Label
objects that assign their text
property through their constructor:
component A{
default children
}
component Label{
constructor(arg:number){
super()
this{}
this.text = arg
}
string text: ''
}
const a = A{
Label.("Label 1"){}
Label.("Label 2"){}
}
In user interfaces, a lot of visual components have to work with text, and most of the time, different parts of the text need to be formated differently. These annotations can become quite hard to follow, it's why Live Elements provides a shortcut to creating these visual components. The previous example, can be simplified as such:
const a = A{
Label`Label 1`
Label`Label 2`
}
Using this string notation makes it easy to format text in Live Elements. We can, for example, write the following:
Paragraph{
T`I am `B`bold`T` and `I`italic.`
}
This creates a paragraph component, with 4 children
.T
, which stands for plain text, B
which is bold text, and I
, which is italic text. A detailed version from above would be written like this:
Paragraph{
T{ text: "I am " }
B{ text: "bold" }
T{ text: " and " }
I{ text: "italic." }
}
Text using this notation can be split on multiple lines. All spaces are trimmed to a single space:
const p = Paragraph{
T`
This is a
paragraph
spread on
multiple lines.
`
}
console.log(p.children[0].text) // This is a paragraph spread on multiple lines
To add spaces or new lines, we can use the \s
and \n
symbols:
const p = Paragraph{
T`
This is a paragraph spread on \n
multiple\s\s lines.
`
}
console.log(p.children[0].text) // This is a paragraph spread on
// multiple lines
Another option to separate lines, is to use tripple quoted strings, which store the raw string (similar to the <pre>
element in html):
const p = Paragraph{
T```
This is a paragraph spread on
multiple\s\s lines.
```
}
console.log(p.children[0].text) // This is a paragraph spread on
// multiple lines
Sometimes you might want to do operations after all assignments and connections have been made to an object. There's a function called completed
which you can override when creating the object:
component A{}
const a = A{
number x: 10
number y: 20
fn completed(){
console.log("x and y are assigned: " + x + "," + y)
}
}
Components can be created at the root of each lv
file in order to be exported for that module. To create a component at the root of the file, the instance
keyword is required together with the object name to be exported before the actual component creation:
// mymodule/myobject.lv
instance myobject A{} // will export an instance of A under the name of myobject
So now, when importing mymodule
myobject
will be available:
import .mymodule
component B{
fn completed(){
console.log(myobject) // object of type A
}
}
The next page goes through working with web components.