In the previous chapter, we've shown how to create a simple counter application. Starting from that example, we will create a reusable counter component to keep track of the number of attendees in a classroom.
Let's assume we have 3 classrooms. and we have to keep track of the number of attendees for each classroom. We will need a list to display each classroom, and next to each item we will need a counter where we can increment or decrement that number.
We can start by simply listing the 3 classrooms as a list:
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'}]
Ul{
Li{ T`Classroom A` }
Li{ T`Classroom B` }
Li{ T`Classroom C` }
}
}
Now, we could start adding increment and decrement buttons to each item in the list, but that would be repeating ourselves too much. What we should do is extract each list as a separate component:
import live-web.dom
import live-elements-web-server.view
import live-elements-web-server.style
component Classroom < Li{
T`Classroom ...`
}
component Index < PageView{
static any[] use = [ScopedStyle{ src: './index.css'}]
Ul{
Classroom{}
Classroom{}
Classroom{}
}
}
Notice we've extended the Li
component, so now Classroom will simply act as an Li
. The actual html output will still be an <li>
. On top of that, we are adding the text T
to each classroom automatically. The only issue is all the classes will now have the same name. We can fix that with a label property:
import live-web.dom
import live-elements-web-server.view
import live-elements-web-server.style
component Classroom < Li{
id: classroom
string label: ''
T{ text: 'Classroom ' + classroom.label }
}
component Index < PageView{
static any[] use = [ScopedStyle{ src: './index.css'}]
Ul{
Classroom{ label: 'A' }
Classroom{ label: 'B' }
Classroom{ label: 'C' }
}
}
We've now created a label property for the Classroom
component. In the Classroom
component, we've setup an id
for the component, then we used the id
to reference the label property when populating the text:
T{ text: 'Classroom ' + classroom.label }
Now we can add the counter to the Classroom
component. We will need an attendeeCount
property to store the number of attendees, an increment button, a decrement button, and a text showing the actual count:
import live-web.dom
import live-elements-web-server.view
import live-elements-web-server.style
component Classroom < Li{
id: classroom
string label: ''
number attendeeCount: 0
T{ text: 'Classroom ' + classroom.label }
Button{ // decrement attendeeCount
on click: () => {
if ( classroom.attendeeCount > 0 )
classroom.attendeeCount--
}
T`-`
}
Span{ T{ text: classroom.attendeeCount } } // display attendeeCount
Button{ // increment attendeeCount
on click: () => { classroom.attendeeCount++ }
T`+`
}
}
component Index < PageView{
static any[] use = [ScopedStyle{ src: './index.css'}]
Ul{
Classroom{ label: 'A' }
Classroom{ label: 'B' }
Classroom{ label: 'C' }
}
}
And we now have a working list of classrooms with counters.
In larger applications, it's a good ideea to have the state separate from the view, as it's easier to manage, read and also access from the view hierarchy. In Live Elements, we do this by simply moving the state properties into their own component. In the counter example, we have move the label
and attendeeCount
properties to a ClassroomData
component:
import live-web.dom
import live-elements-web-server.view
import live-elements-web-server.style
component ClassroomData{
string label: ''
number attendeeCount: 0
}
component Classroom < Li{
id: classroom
ClassroomData data: ClassroomData{}
T{ text: 'Classroom ' + classroom.data.label }
Button{
on click: () => {
if ( classroom.data.attendeeCount > 0 )
classroom.data.attendeeCount--
}
T`-`
}
Span{ T{ text: classroom.data.attendeeCount } }
Button{
on click: () => { classroom.data.attendeeCount++ }
T`+`
}
}
component Index < PageView{
static any[] use = [ScopedStyle{ src: './index.css'}]
Ul{
Classroom{ data: ClassroomData{ label: 'A' } }
Classroom{ data: ClassroomData{ label: 'B' } }
Classroom{ data: ClassroomData{ label: 'C' } }
}
}
Classroom
now has a data
assigned with a ClassroomData
instance, which we now access via classroom.data
.
In this chapter we've looked at how to create reusable components and separate their data. Next, we will look at styling these components.