In the previous chapter, we've learned how to setup a playground, and how to run a console application inside the playground. We've also introduced components.
In this chapter we will work with the DOM in Live Elements.
In Live Elements, each html tag or (DOM element) is a separate component. For example. the <p>
tag corresponds to the P
component. The <h1>
tag corresponds to the H1
component. All of these DOM components, like the H1
and P
are declared in live-web.dom
module. We've already learned that creating a component is done by using curly braces after the component name, so creating an html elements like <h1>
is simply created like this: H1{}
.
Let's try it out in the playground. We need to import live-web.dom
and we can create a paragraph:
import live-web
import live-web.dom // import live-web.dom to make P available
component App < Application{
run: () => {
const p = P{} // create P
console.log(p)
}
}
We will see the console output: [object Object]
.
This is great, but we should add this paragraph to our page so we can see it. Let's first add some text to our paragraph. We can do this using the text T
component:
import live-web
import live-web.dom
component App < Application{
run: () => {
const p = P{ T{ text: 'Hello' } }
console.log(p)
}
}
Then, we can use P.expandTo()
function, which takes a javascript dom element. We'll use document.body
:
import live-web
import live-web.dom
component App < Application{
run: () => {
const p = P{ T{ text: 'Hello' } }
p.expandTo(document.body)
}
}
The paragraph is now visible on our web page! This is great, let's replace it with a title:
import live-web
import live-web.dom
component App < Application{
run: () => {
const h1 = H1{ T{ text: 'Hello' } }
h1.expandTo(document.body)
}
}
Great, now the 'Hello' text is a title! Let's add both the title and paragraph:
import live-web
import live-web.dom
component App < Application{
run: () => {
const h1 = H1{ T{ text: 'Hello' } }
h1.expandTo(document.body)
const p = P{ T{ text: 'World' } }
p.expandTo(document.body)
}
}
The title is not visible, and that's because Live Elements clears the html dom node on which it expands. So, when p
is expanded, the h1
that was appended to the document.body
is now cleared. To show both the title and paragraph, we can wrap everything inside a Div
and then expand the Div
to the document.body
:
import live-web
import live-web.dom
component App < Application{
run: () => {
const div = Div{
H1{ T{ text: 'Welcome' } }
P{ T{ text: 'Hello' } }
}
div.expandTo(document.body)
}
}
Notice we've added H1
and P
as children to the Div
. We've introduced components that support children in the previous chapter.
We can make use of another concept to shorten our code even more. Creating the T
component is quite long:
T{ text: 'Welcome' }
The T
component however has a constructor taking a string argument. We can create it this way:
T`Welcome`
The application code now becomes:
import live-web
import live-web.dom
component App < Application{
run: () => {
const div = Div{
H1{ T`Welcome` }
P{ T`Hello` }
}
div.expandTo(document.body)
}
}
It's shorter, but we can do better. P
and H1
also have constructors taking a string argument:
import live-web
import live-web.dom
component App < Application{
run: () => {
const div = Div{
H1`Welcome`
P`Hello`
}
div.expandTo(document.body)
}
}
Both components will create a T
element internally when they are created this way. Not all components support this, Div
for example doesn't have a constructor taking a string argument, so Div`Text`
won't create the text with the div.
Components in Live Elements can declare events, and also listeners for those events. In live-web.dom
all components have all the DOM events available in html: click, mousenter, mouseleave, focus, load, resize, etc. We can listen to those events using the on <eventName>
syntax. Let's create a Button
to see how this works:
import live-web
import live-web.dom
component App < Application{
run: () => {
const div = Div{
H1`Welcome`
P`Hello`
Button{
on click: () => { console.log('Clicked') }
T`Click me`
}
}
div.expandTo(document.body)
}
}
The on click
listener takes a function as it's argument, and will execute that function every time the button is clicked.
In the previous chapter we've learned about property bindings. Using the Button
and the concept of property bindings we can create a counter in Live Elements quite easily:
import live-web
import live-web.dom
component App < Application{
run: () => {
// declare a Counter
component Counter{
number value: 0
};
// create the counter
const counter = Counter{}
const div = Div{
// bind to counter value property
P{ T{ text: counter.value }}
Button{
on click: () => { counter.value++ }
T`Increment`
}
}
div.expandTo(document.body)
}
}
T.text
property is bound to counter.value
. Every time the button is clicked, we increment the value, which will change the text
property.
You will notice the playground also has a stylesheet file index.css
. Modifying the file, like for example making the paragraph red: p{ text: red; }
will have no effect. This is because we are not referencing the stylesheet anywhere in Index.lv
. The console application won't be able to directly do that. This is why we will switch to a new project structure, one used specifically to create Live Elements pages. You can go to the 'Open' icon, and select 'New Page'.
This will open up the following Index.lv
file:
import live-web.dom
import live-elements-web-server.view
import live-elements-web-server.style
component Index < PageView{
static any[] use = [ScopedStyle{ src: './index.css'}]
}
Here the Index
component inherits PageView
. PageView
does not have a run
function, but rather it supports adding children as part of the page. Try appending the following header and save the file:
import live-web.dom
import live-elements-web-server.view
import live-elements-web-server.style
component Index < PageView{
static any[] use = [ScopedStyle{ src: './index.css'}]
H1`Hello`
}
The 'Hello' message should now be visible. To transfer the counter example we implemented above, we can add the counter as a property to the Index
component:
import live-web.dom
import live-elements-web-server.view
import live-elements-web-server.style
component Counter{
number value: 0
}
component Index < PageView{
id: index // this can now be referenced via 'index'
static any[] use = [ScopedStyle{ src: './index.css'}]
// counter property is now a new counter component
Counter counter: Counter{}
Div{
P{ T{ text: index.counter.value }}
Button{
on click: () => { index.counter.value++ }
T`Increment`
}
}
}
We declare the Counter
component outside of the Index
component. We also define an id for the Index
component so we can reference it in it's children. Then we access the counter value property via index.counter.value
.
Notice the Index
component also defines a static property use
:
static any[] use = [ScopedStyle{ src: './index.css'}]
The property let's the server know it needs to include the index.css
file as part of the page. Whenever the server will load this PageView
, it will also include the index.css
stylesheet.
We can now style the page via the index.css
file:
p{
color: blue;
font-size: 30px;
}
To set a class for the paragraph we can use the classes
property, which takes an array of classes:
P{
classes: ['text-large', 'border-blue']
}
This is equivalent to the following html:
<p class="text-large border-blue"></p>
We can modify the previous example to color the number blue if the number is even, or purple otherwise:
import live-web.dom
import live-elements-web-server.view
import live-elements-web-server.style
component Counter{
number value: 0
}
component Index < PageView{
id: index // this can now be referenced via 'index'
static any[] use = [ScopedStyle{ src: './index.css'}]
// create the counter component
Counter counter: Counter{}
Div{
P{ classes: [index.counter.value % 2 === 0 ? 'even' : 'odd']
T{ text: index.counter.value }
}
Button{
on click: () => { index.counter.value++ }
T`Increment`
}
}
}
And the css:
p.odd{
color: blue;
font-size: 30px;
}
p.even{
color: purple;
font-size: 30px;
}
All components in live-web.dom
besides the text node T
inherit from DOMElement
from which they share the following properties:
id
in html.class
attribute in html.style
attribute in html.As an example on how to use some of these properties, the following paragraph is declared in both Live Elements and html:
P{ classes: ['paragraph'] glid: 'main-paragraph' style: { marginLeft: '10px'} props = { data = { info='custom-paragraph' } } }
<p class="paragraph" id="main-paragraph" style="margin-left:10px;" data-info="custom-paragraph" ></p>
Additionaly to the properties shared from DOMElement
, some components add their own properties. For example the anchor A
component has the href
property:
A{ href: '/link/to/other/page' T`Link to other page` }
This chapter covered using Live Elements to work with the html DOM, styling the page and working with events and user interactions. The following chapter will cover creating reusable components.