Press "Enter" to skip to content

Elektron nəşr texnologiyaları lit html formatlari

You should now see three todo items on the screen, where the first one is already finished.

The “website” tab on the right side should automatically update and show the

Todo app

rendered from your web component.

Templates are javascript variables, and can be created and composed like other variables.

We can also create them outside of our component’s context. A good example of this is when you want to share pieces of a template between different components.

Let’s add a footer to our application. First, let’s create the template for the footer:

You can add your own name and website in there as author.

Template literals can contain placeholders. These are indicated by a dollar sign with curly braces: $ .

Template literals are often used when building error messages:

// regular string console.error('An error occurred: ' + message); // template literal console.error(`An error occurred: $`); 

Lit takes advantage of this feature in order to compose templates and to create dynamic parts inside your templates. For example, we can add the footer to our app’s template by simply embedding it:

class TodoApp extends LitElement < render() < return html` 

Todo app

$ `; > >

You should now see both the app’s title and footer on the page.

Lit supports embedding different types of variables. In the example above, we embedded a template inside of another template, but we can also embed strings inside of a template. Let’s extract the link text to a separate variable and then embed it in the template:

const author = 'open-wc'; const footerTemplate = html` 
Made with love by $
`;

We can also extract the link to a separate variable, and set the href attribute with a variable:

const author = 'open-wc'; const homepage = 'https://open-wc.org/'; const footerTemplate = html` 
Made with love by ">$
`;

When embedding variables like this, Lit remembers which parts of your template are static and which parts are dynamic. When re-rendering the same template you can change the value of these variables and Lit will know to only update the parts that changed. This makes it very efficient!

It’s important to keep in mind that whatever you’re writing must still be valid HTML and you cannot arbitrarily concatenate strings to build HTML. This is important to enable efficient updates, as well as for security to protect you from XSS attacks.

For example you cannot set tagnames or attribute keys dynamically:

View final result

import < LitElement, html >from 'lit'; const author = 'open-wc'; const homepage = 'https://open-wc.org/'; const footerTemplate = html` 
Made with love by ">$
`; class TodoApp extends LitElement < render() < return html`

Todo app

$ `; > > customElements.define('todo-app', TodoApp);

Now that you know how to compose templates with Lit, we can start adding a list of todos to our application.

Let’s start by creating an array of todos in the constructor of our component:

class TodoApp extends LitElement < constructor() < super(); this.todos = ['Do A', 'Do B', 'Do C']; >> 

We can render this array directly inside the template of our application:

render() < return html` 

Todo app

$ $ `; >

When you pass an array to Lit, it will just iterate and render what’s inside it. In this case, it will render the list of todos as plain text.

Just displaying text is not what we want though, we need something more complex. This is where we can combine two capabilities of Lit: iterating arrays and rendering nested templates. If we turn our array of strings to an array of templates, we can render HTML for each of our todos.

A great way to accomplish this is through a map function. Let’s create an ordered list of todos:

Apart from displaying the text of a todo item, we need to indicate whether the todo item is finished or not.

Let’s update our data structure from strings to objects and display the finished state on the screen:

this.todos = [ < text: 'Do A', finished: true >, < text: 'Do B', finished: false >, < text: 'Do C', finished: false >, ]; 

Because template literals allow us to place any expression inside of the curly braces, we can use ternary operators for quick and easy conditional logic.

You should now see three todo items on the screen, where the first one is already finished.

View final result

Now, we will add the ability to add todos to our list.

Start by adding an input field and a button:

On the “add” button we attached an event listener that listens for the click event. This is done by prefixing the event name with a @ and attributing a function to it.

html`  `; 

This is just syntactic sugar that executes the addEventListener() function on the element with the specified event and function. In this case, we reference a function of our component, which we should now implement:

_addTodo() < const input = this.shadowRoot.getElementById('addTodoInput'); const text = input.value; input.value = ''; this.todos.push(< text, finished: false >); this.requestUpdate(); > 

When this event handler is called, we create a new todo item and add it to the array of todos. Next we need to trigger a re-render so that we can display the new todo item on the screen. We can do this by calling the requestUpdate method which exists on any element that extends from the LitElement class.

When you click add, you should see the new element appear on the screen.

This allows us to observe the awesome power of Lit in action. If you inspect the DOM while adding a new todo item, you will notice that only the new todo item is flashing:

When something in the DOM inspector flashes, it means that the browser is doing actual work to update the DOM tree. This is very expensive, things like the styles and layout need to be recalculated up and down the element tree, so it is wise to minimize this as much as possible. Lit knows exactly what changed where and it will update only that part, making it super efficient.

In the inspector you also see comment nodes between different parts of your template. These are markers created by Lit to track the locations of DOM nodes, they can be ignored safely. View final result

Right now, we’re triggering updates manually whenever we make a change. This is fine for some use cases, but it can get pretty cumbersome and we are not able to respond to changes triggered by parent components.

It’s better to let LitElement observe data changes for us and, trigger updates when necessary. We can do this by defining todos as a property of our element.

Start by adding a static properties field, and add todos as an array property:

static properties = < todos: < type: Array >>; 

For each property that you’ve defined, LitElement generates something similar to this:

class TodosApp extends LitElement < set todos(newTodos) < if (this._todos === newTodos) < // no change, don't do any work return; >// value changed, trigger an update this._todos = newTodos; this.requestUpdate(); > get todos() < return this._todos; >> 

This way when you change the property on your element, it goes through a custom getter/setter function which triggers an update only when the new value passes a simple equality check.

For strings, numbers and booleans this will work without any problems. However, if you are using arrays or objects and mutate them, it will not trigger any update. This is because the actual array or object itself did not change. We need to use immutable data patterns, where a new object is created for each change. This is a common pattern in front-end to simplify data flow and make change detection easier.

In our case we are using Array’s push() function, which mutates the existing this.todos array. In order to use an immutable data pattern, instead, we can copy the existing list of todos using array spread, add our new todo and assign it back to this.todos :

_addTodo() < const input = this.shadowRoot.getElementById('addTodoInput'); const text = input.value; input.value = ''; this.todos = [ . this.todos, < text, finished: false >, ]; > 

The list should now still update like before.

View final result

If we make a mistake, we want to be able to remove a todo item from the list.

Let’s add a delete button to the template of a todo item:

We need to pass along the item we want to delete to the event handler, so instead of referencing the method directly we are using an arrow function and we call it with the item of the current iteration of our map function.

Next, we add the event handler which deletes the todo item:

_removeTodo(todo) < this.todos = this.todos.filter(e =>e !== todo); > 

The delete button should now be fully functional. In this function as well, we assign a new array to this.todos as filter() returns a new array. LitElement will automatically detect the new array and re-render our component.

View final result

A todo list is useless if we can’t keep track of which todos we’ve finished, and which todos we have not. What we need is a way to manage its state.

First, let’s replace our finished/unfinished text with a checkbox:

Notice that we prefixed the checked attribute on the checkbox with a . . This is special Lit syntax to specifiy we want to set the property named checked instead of the attribute named checked .

Attributes are how we can assign (string) data in the HTML representation of an element:

Properties are how we can assign data in javascript, on the actual DOM element in Javascript:

const input = /* get a reference to the input element */; input.value = 'foo'; 

We can also access or set attribute data in Javascript.

const input = /* get a reference to the input element */; input.setAttribute('value', 'foo'); 

The concept of attributes and properties of a HTML element confuses a lot of people, as it is sometimes thought to represent the same data and to be interchangeable. It’s up to each element on how (and if) to keep the value of properties and attributes in sync. A common practice is to sync changes to an attribute to a property of the same name but not to reflect changes to properties to an attribute of the same name.

We’re listening to the input’s change event to update our data when the checkbox value is changed. Besides the todo object, we are also passing along the event object itself. We need this to be able to get the value of the checkbox.

In the event handler, we can use the map() function to update the finished property of our todo:

_changeTodoFinished(e, changedTodo) < const finished = e.target.checked; this.todos = this.todos.map((todo) => < if (todo !== changedTodo) < return todo; >return < . changedTodo, finished >; >); > 

View final result

Now that we can manage our todo items, it’s pretty easy to display some more information about our list. For example, we can add the total amount of finished and unfinished items.

These are not new sources of data, but rather a simple calculation based on existing data. We could do these calculations inline in our template where we want to display them, but this can get messy, pretty quickly.

Good practice is to use the top of your render function as a place to prepare variables with some meaningful names to use them in your template. This provides the best readability.

Let’s add our calculations to the render function, and display the calculated amounts in the template:

Remember that the render() function can be called quite often. If the computations are expensive, it’s better to only do them once and cache the results.

View final result

It looks like we’re feature complete! We can display a list of todos, add or remove them and check them off once completed.

However, we’ve been putting everything inside only one component, and it’s starting to get crowded. It’s better to split functionality up into separate components. We are building Web Components, after all.

The todo list is an ideal candidate to be moved into a separate component. Let’s go ahead and define the basic structure of our element. If you’re working in an online editor, it’s probably easier to do this in the same file. If you’re using a local editor, it’s best to create a separate file for this.

If you use a separate file, don’t forget to import LitElement and html for that module as well.

The structure of the class should be pretty familiar by now. It has a todos property and a template to render. The render() function looks similar as before, except that there is an if condition now at the top of the function. We need this because, unlike before, our todo list is not in charge of the data anymore.

The parent element is still in charge, and we expect that it will pass along the todos list to this component. This means that we can’t assume that the list will always be there when rendered. If we don’t take care of this, our component will crash because you can’t run a map function on undefined . Adding an early return to the render function is a simple way to do this. It makes it easy to see which properties are required for rendering.

Next, we need to somehow let the parent element know that the user clicked on the checkbox or the remove button. We can do this by using DOM events. DOM events are great because the structure and hierarchy of our application is reflected in the DOM. When an event gets fired on a component, only parent components up the DOM tree can receive it. This behavior allows our communication system to be automatically scoped.

Let’s add the events that we want to fire:

_changeTodoFinished(e, changedTodo) < const eventDetails = < changedTodo, finished: e.target.checked >; this.dispatchEvent(new CustomEvent('change-todo-finished', < detail: eventDetails >)); > _removeTodo(item) < this.dispatchEvent(new CustomEvent('remove-todo', < detail: item >)); > 

The next step will be to actually use the new child component. We will need to pass on the appropriate data and listen to the events fired by the child.

View final result

Now that we’ve created our child element, we need to implement it in the parent element. If you created your child element in a separate file, you will need to import it in the parent element:

import './todos-list.js'; 

Some online editors automatically include separate files.

To render our child component, we simply replace the existing template with the tag of the child component:

render() < const finishedCount = this.todos.filter(e =>e.finished).length; const unfinishedCount = this.todos.length - finishedCount; return html` 

Todo app

> Total finished: $ Total unfinished: $ $ `; >

If you refresh, the UI should remain unchanged. If that is the case, congratulations! You’re now composing elements like a pro. 🙂

Notice that we are again using the property syntax to pass on the data in this.todo to the element.

Next, we need to listen to the new events of :

 

The events are calling the existing methods that we already have defined in our element. However, we will need to update the event handlers slightly to handle these new events:

_removeTodo(e) < this.todos = this.todos.filter(todo =>todo !== e.detail); > _changeTodoFinished(e) < const < changedTodo, finished >= e.detail; this.todos = this.todos.map((todo) => < if (todo !== changedTodo) < return todo; >return < . changedTodo, finished >; >); > 

After this, your application should work just like before, but the code is not as spread out anymore.

We’ve covered the basics of templating and managing data with lit-element. The last remaining topic we need to look into is styling. This isn’t a codelab on CSS, so we will only look at some of the specifics of working with styling in lit-element.

For styling, lit-element uses Shadow DOM. If you’re not familiar with Shadow DOM, I recommend following the web component basics codelab.

To define the styles of your element we need to import the css tag and add a static styles property on our element. Let’s add styles to the todo list:

The styles we define here only apply to our element. This is because we’re using Shadow DOM. Lit-element doesn’t need to do any extra work. This means we can write simple CSS selectors, and we don’t need to worry about causing conflicts with styles defined elsewhere in the page.

:host is a special selector, which selects the host of the shadow root that these styles are associated with. In our case that’s our own custom element. View final result

And that’s it! We’ve gone through the basics of lit-element and Lit. Hopefully from here onwards you will be able to move forward working with lit-element and Lit with confidence.

If you’re eager to learn more, you can take a look at the following resources:

  • Lit official docs
  • lit-element official docs
  • open-wc code samples
  • IDE help

To get started with your own project we recommend using open-wc’s project scaffolding, it’s easy to set it up using this command:

npm init @open-wc 

lit-html 2.0

lit-html is the template system that powers the Lit library for building fast web components. When using lit-html to develop web components, most users should import lit-html via the lit package rather than installing and importing from lit-html directly.

About this release

This is a stable release of lit-html 2.0 (part of the Lit 2.0 release). If upgrading from previous versions of lit-html , please note the minor breaking changes from lit-html 1.0 in the Upgrade Guide.

Documentation

Full documentation is available at lit.dev.

Overview

lit-html lets you write HTML templates in JavaScript with template literals.

lit-html templates are plain JavaScript and combine the familiarity of writing HTML with the power of JavaScript. lit-html takes care of efficiently rendering templates to DOM, including efficiently updating the DOM with new values.

import html, render> from 'lit-html'; // This is a lit-html template function. It returns a lit-html template. const helloTemplate = (name) => html`div>Hello $name>!div>`; // This renders 
Hello Steve!
to the document body
render(helloTemplate('Steve'), document.body); // This updates to
Hello Kevin!
, but only updates the $ part
render(helloTemplate('Kevin'), document.body);

lit-html provides two main exports:

  • html : A JavaScript template tag used to produce a TemplateResult , which is a container for a template, and the values that should populate the template.
  • render() : A function that renders a TemplateResult to a DOM container, such as an element or shadow root.

Installation

$ npm install lit-html

Comments are closed, but trackbacks and pingbacks are open.