# 🇬🇧 LitElement - First steps - Part II
updated on 2019-01-13 with LitElement 2.0.0-rc.2
Yesterday I explained how to set up a LitElement project with a first main component. Now, it's time to add more components (or elements).
# A title component
We are going to create a title component <my-title></my-title>
. So in the /src
directory add a new JavaScript file my-title.js
with this source code:
import { LitElement, html } from 'lit-element'
export class MyTitle extends LitElement {
constructor() {
super()
}
render(){
return html`
<h1>
Kitchen Sink 🍔 [LitElement]
</h1>
`
}
}
customElements.define('my-title', MyTitle)
We need to load this new component, so in the index.html
file, update this part of source code with adding import('./src/my-title.js')
:
<script type="module">
window.WebComponents = window.WebComponents || {
waitFor(cb){ addEventListener('WebComponentsReady', cb) }
}
WebComponents.waitFor(async () => {
import('./src/main-application.js')
import('./src/my-title.js')
});
</script>
And one last thing, we have to "insert" this title element inside the main element, so edit and update main-application.js
like that:
import { LitElement, html } from 'lit-element'
export class MainApplication extends LitElement {
constructor() {
super()
}
render() {
return html`
<section>
<div>
<my-title></my-title>
</div>
<section>
`
}
}
customElements.define('main-application', MainApplication);
Now, you can run this command: polymer serve
at the root of the project, and reach this url http://127.0.0.1:8081 (opens new window), you'll get a great 🎉 title:
# Play with properties
Now we'll add a "sub-title", but I would like to set the content of the element with an html attribute, like that:
<my-sub-title my-text="I ❤️ LitElement"></my-sub-title>
For that, we're going to use a static property getter, something like that:
static get properties() {
return {
counter: { type: Number }
}
}
You can read more in the official documentation here: https://lit-element.polymer-project.org/guide/properties (opens new window)
In the /src
directory, add a new JavaScript file my-sub-title.js
with this source code:
import { LitElement, html } from 'lit-element'
export class MySubTitle extends LitElement {
static get properties() {
return {
myText: { attribute: 'my-text' }
}
}
constructor() {
super()
}
render(){
return html`
<h2>
${this.myText}
</h2>
`
}
}
customElements.define('my-sub-title', MySubTitle)
So, this: myText: { attribute: 'my-text' }
allows us to make the "link" between this.myText
in the JavaScript class andt the html attribute my-text
from <my-sub-title my-text="I ❤️ LitElement">
on the html side.
In the index.html
file, update the source code like that:
<script type="module">
window.WebComponents = window.WebComponents || {
waitFor(cb){ addEventListener('WebComponentsReady', cb) }
}
WebComponents.waitFor(async () => {
import('./src/main-application.js')
import('./src/my-title.js')
import('./src/my-sub-title.js')
});
</script>
And finally, add the new element inside the main element, so edit and update main-application.js
like that:
import { LitElement, html } from 'lit-element'
export class MainApplication extends LitElement {
constructor() {
super()
}
render() {
return html`
<section>
<div>
<my-title></my-title>
<my-sub-title my-text="I ❤️ LitElement"></my-sub-title>
</div>
<section>
`
}
}
customElements.define('main-application', MainApplication);
Go to http://127.0.0.1:8081 (opens new window), and you can see that our web application is becoming better and better 😉:
# On Click!
There is no proper tutorial without a button and a click handler. Then, create a new JavaScript file src/my-amazing-button.js
with this content:
import { LitElement, html } from 'lit-element'
export class MyAmazingButton extends LitElement {
static get properties() {
return {
counter: { type: Number }
}
}
constructor() {
super()
this.counter = 0
}
render(){
return html`
<div>
<button @click="${this.clickHandler}">👋 Click me! ${this.counter}</button>
</div>
`
}
clickHandler() {
this.counter+=1
}
}
customElements.define('my-amazing-button', MyAmazingButton)
So, it's simple, you just have to add this to a tag: <button @click="${this.clickHandler}">
and create the clickHandler()
method (you can name it as you want of course). You can see that I added a counter
property, so guess what? ... counter
will be incremented at each click.
Remark: 👋 when you declare a property, you need to initialize it in the class constructor. There are some TypeScript helpers (decorators) to do that, but righ now, I prefer to stay with a good old JavaScript.
You have to reference it in the index.html
file:
<script type="module">
window.WebComponents = window.WebComponents || {
waitFor(cb){ addEventListener('WebComponentsReady', cb) }
}
WebComponents.waitFor(async () => {
import('./src/main-application.js')
import('./src/my-title.js')
import('./src/my-sub-title.js')
import('./src/my-amazing-button.js')
});
</script>
and add it to the main component render()
method:
render() {
return html`
<section>
<div>
<my-title></my-title>
<my-sub-title my-text="I ❤️ LitElement"></my-sub-title>
<my-amazing-button></my-amazing-button>
</div>
<section>
`
}
Go to http://127.0.0.1:8081 (opens new window), and click on your awesome button:
# Display a list
Now, a little bit more difficult (I'm joking), I want to display a list of item in my web application.
Our new component (in src/buddies-list.js
) looks like that:
import { LitElement, html } from 'lit-element'
export class BuddiesList extends LitElement {
static get properties() {
return {
buddies: { type: Array }
}
}
constructor() {
super()
this.buddies = [
"🦊 Foxy",
"🦁 Leo",
"🐯 Tigrou"
]
}
render(){
return html`
<div>
${this.buddies.map(buddy =>
html`<h2>${buddy}</h2>`
)}
</div>
`
}
}
customElements.define('buddies-list', BuddiesList)
😃 The simplicity of the LitElement template is clever ✨, you just need to use the native JavaScript features, like the map
of array
in our case.
Of course, you need to register in the index.html
file and add it to our main component render
method (<main-application>
):
<script type="module">
window.WebComponents = window.WebComponents || {
waitFor(cb){ addEventListener('WebComponentsReady', cb) }
}
WebComponents.waitFor(async () => {
import('./src/main-application.js')
import('./src/my-title.js')
import('./src/my-sub-title.js')
import('./src/my-amazing-button.js')
import('./src/buddies-list.js')
});
</script>
render() {
return html`
${style}
<section>
<div>
<my-title></my-title>
<my-sub-title my-text="I ❤️ LitElement"></my-sub-title>
<my-amazing-button></my-amazing-button>
<hr>
<buddies-list></buddies-list>
</div>
<section>
`
}
And finally, you can admire the result on http://127.0.0.1:8081 (opens new window)
# My web application is ugly 😢
Never mind, there are several ways to style LitElements. I will choose the easiest way (to my mind). We are going to define our css styles in a JavaScript expression inside a LitElement template (that means that you can use JavaScript expressions inside your styles 😍). We'll write that in a new file: /main-styles.js
.
⚠️ Pay attention: I'm not confident with my css skills, nor I don't speak css fluently, so be indulgent.
import { html } from 'lit-element'
export const style = html`
<style>
.container
{
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
text-align: center;
}
.title
{
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
display: block;
font-weight: 300;
font-size: 100px;
color: #35495e;
letter-spacing: 1px;
}
.subtitle
{
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
font-weight: 300;
font-size: 42px;
color: #526488;
word-spacing: 5px;
padding-bottom: 15px;
}
.button {
font-family: "Source Sans Pro", "Helvetica Neue", Arial, sans-serif;
background-color: white;
color: #35495e;
border: 2px solid #35495e;
border-radius: 4px;
padding: 10px 32px;
text-align: center;
text-decoration: none;
display: inline-block;
font-weight: 600;
font-size: 30px;
}
.button:hover {
background-color: #35495e;
color: white;
}
</style>
`
And now, you need to update all your components, importing import {style} from './main-styles.js'
and adding css class information to the tags in the render()
method and an expression ${style}
to include the css styles, like that:
main-application.js
- add
import {style} from './main-styles.js'
- add
${style}
to the template string- add
class="container"
to thesection
tag
import { LitElement, html } from 'lit-element'
import {style} from './main-styles.js'
export class MainApplication extends LitElement {
constructor() {
super()
}
render() {
return html`
${style}
<section class="container">
<div>
<my-title></my-title>
<my-sub-title my-text="I ❤️ LitElement"></my-sub-title>
<my-amazing-button></my-amazing-button>
<hr>
<buddies-list></buddies-list>
</div>
<section>
`
}
}
customElements.define('main-application', MainApplication);
my-title.js
- add
import {style} from './main-styles.js'
- add
${style}
to the template string- add
class="title"
to theh1
tag
import { LitElement, html } from 'lit-element';
import {style} from './main-styles.js';
export class MyTitle extends LitElement {
constructor() {
super()
}
render(){
return html`
${style}
<h1 class="title">
Kitchen Sink 🍔 [LitElement]
</h1>
`
}
}
customElements.define('my-title', MyTitle)
my-sub-title.js
- add
import {style} from './main-styles.js'
- add
${style}
to the template string- add
class="subtitle"
to theh2
tag
import { LitElement, html } from 'lit-element';
import {style} from './main-styles.js';
export class MySubTitle extends LitElement {
static get properties() {
return {
myText: { attribute: 'my-text' }
}
}
constructor() {
super()
}
render(){
return html`
${style}
<h2 class="subtitle">
${this.myText}
</h2>
`
}
}
customElements.define('my-sub-title', MySubTitle)
my-amazing-button.js
- add
import {style} from './main-styles.js'
- add
${style}
to the template string- add
class="button"
to thebutton
tag
import { LitElement, html } from 'lit-element';
import {style} from './main-styles.js';
export class MyAmazingButton extends LitElement {
static get properties() {
return {
counter: { type: Number }
}
}
constructor() {
super()
this.counter = 0
}
render(){
return html`
${style}
<div>
<button @click="${this.clickHandler}" class="button">👋 Click me! ${this.counter}</button>
</div>
`
}
clickHandler() {
this.counter+=1
}
}
customElements.define('my-amazing-button', MyAmazingButton)
buddies-list.js
- add
import {style} from './main-styles.js'
- add
${style}
to the template string- add
class="subtitle"
to theh2
tag
import { LitElement, html } from 'lit-element'
import {style} from './main-styles.js';
export class BuddiesList extends LitElement {
static get properties() {
return {
buddies: { type: Array }
}
}
constructor() {
super()
this.buddies = [
"🦊 Foxy",
"🦁 Leo",
"🐯 Tigrou"
]
}
render(){
return html`
${style}
<div>
${this.buddies.map(buddy =>
html`<h2 class="subtitle">${buddy}</h2>`
)}
</div>
`
}
}
customElements.define('buddies-list', BuddiesList)
And we obtain an incredible fancy web application on http://127.0.0.1:8081 (opens new window)
# Building 🙀
It's time to build for production (I will write something later (days or weeks) about the real production deployment of a LitElement project). So type these commands
polymer build
polymer serve build/default
And go to http://127.0.0.1:8081 (opens new window), and ...
... Nothing. It's normal, I forgot to update the polymer.json
file with the new files of my project. This is the update we need:
{
"shell": "src/main-application.js",
"entrypoint": "index.html",
"fragments": [
"src/my-title.js",
"src/my-sub-title.js",
"src/my-amazing-button.js",
"src/buddies-list.js"
],
"npm": true,
"moduleResolution": "node",
"sources": [
"src/main-application.js",
"src/my-title.js",
"src/my-sub-title.js",
"src/my-amazing-button.js",
"src/buddies-list.js",
"src/main-styles.js",
"package.json"
],
"extraDependencies": [
"node_modules/@webcomponents/webcomponentsjs/**"
],
"builds": [{
"bundle": true,
"js": {
"minify": false,
"compile": "es5",
"transformModulesToAmd": true
},
"addServiceWorker": true,
"addPushManifest": true
}]
}
Build ans serve again:
polymer build
polymer serve build/default
And now it's better 😍
That's all for today. If you need all the files of the project, it's here https://gitlab.com/bots-garden/training-materials/bootstrapping-a-litelement-project/tree/01-more-components (opens new window).
See you tomorrow 👋 (or later, sometimes I need to sleep). We'll talk about how we can made the components communicate.
Have a nice day 😃
Last Articles
- 🇫🇷 Type Result en Kotlin | 2020-10-31 | Kotlin
- 🇫🇷 Type Result en Kotlin | 2020-10-31 | Kotlin
- 🇬🇧 Every GitLab Page deserves a real CI/CD | 2020-07-23 | GitLab CI
- 🇫🇷 Lit-Element, commencer doucement | 2020-07-20 | WebComponent
- 🇬🇧 Build quickly and host easily your Docker images with GitLab and GitLab CI | 2020-06-02 | GitLab CI
- 🇬🇧 Deploy quickly on Clever Cloud with GitLab CI | 2020-05-31 | GitLab CI
- 🇫🇷 Borg Collective, mes jouets pour apprendre Knative | 2020-05-30 | Knative
- 🇬🇧 Borg Collective, Toys to learn Knative | 2020-05-30 | Knative
- 🇫🇷 M5Stack, une petit device IOT bien sympathique, programmable en Python | 2020-05-09 | IOT
- 🇫🇷 Knative, l'outil qui rend Kubernetes sympathique | 2020-05-02 | kubernetes