30

Intro To Web Components in 2019

 5 years ago
source link: https://www.tuicool.com/articles/hit/vUrUj26
Go to the source link to view the article. You can view the picture content, updated content and better typesetting reading experience. If the link is broken, please click the button below to view the snapshot at that time.

Intro To Web Components in 2019

Understanding modern web components to build a simple app

Ubqu22V.png!webf6vmy2r.png!web

Nowadays it feels like everyone is crazy about code reusability. The developer’s ability to create reusable components helps them establish some kind of common pattern or convention in their application.

We even see open source tools like Bit take reusability to the next level, so you can develop, share and sync components at scale between your apps.

After all, it would be better for you as a developer if you didn’t have to build the same button component again and again in your project, with just some minor changes between each of them, right?

In my honest opinion, a truly reusable component is one that can be used anywhere and everywhere - The component built in one framework/library can be inserted into another framework/library.

But every framework/library has its own sets of rules that the developer needs to follow. And until now code reusability has been easier to follow if you are working in just a single framework/library. But there are things can Vue can do that Angular and React can’t, and vice versa.

In some of my previous posts, I showed you how Angular has provided us with Angular Elements that can be used to insert an Angular component as a custom element into React or Vue.

But this requires you to have a certain level of understanding of how Angular Elements works.

What we truly need, is something simpler that works in any latest browser and in any JavaScript library/framework.

Enter Web Components!

ueMrUzy.jpg!webmeUFNbI.jpg!web

Web Components is a group of features that allow us to create completely reusable components. In this post, we will take a look at how web components work and how you can use them in your projects.

Web Components are really hot right now, not only because major companies like Netflix and Youtube are already using them, but also because Microsoft recently announced that Edge will soon provide native support to Custom Elements, which is one of the APIs that is used to create web components.

RzUbYrA.png!webEbqQn2F.png!web
Click here to keep track of all the things that Edge supports

Web Components — What Are They?

As mentioned before, web components are a group of features that let us create reusable components. The best thing about these components is that they nothing more than HTML elements, which means you do not have to install any new framework or library to use them. All you need is pure vanilla javascript!

Web components are created using these primary APIs:

  • Custom Elements
  • Shadow DOM
  • HTML Template

Let’s take a quick look at what each of these technologies are:

Custom Elements

The Custom Elements API is what we actually use to create our custom HTML elements and attach any desired functionality to that element. It lays the foundation on which we can design and use our own elements.

Shadow DOM

This API with a cool ninja-like name of Shadow DOM helps the developer enclose things like styling and markup of the components. Through Shadow DOM, the web browser renders the elements without putting them on the main DOM tree. This is done by creating a sub-DOM tree that is attached to the main DOM tree.

HTML Template

The template is an HTML tag inside which we can write reusable chunks of code. Nothing inside the template will run, load, or render until it is activated by stamping out the template.

Web Component Lifecycle Methods

Like any other component in Vue or React, Web Components also have a few lifecycle methods. They are:

  • Constructor
  • connectedCallback
  • disconnectedCallback
  • attributeChangedCallback
  • adoptedCallback

The constructor function runs when the component is created, but has not been inserted into the DOM. Like in other frameworks and libraries, constructor is a great place to initialize the state, and we can even set event listeners, creating the Shadow DOM for the component.

When the component is inserted into the DOM, the connectedCallback function will be executed. It is similar to componentDidMount from React, and we can use to do things like fetching data from an API.

As the name probably suggests, disconnectedCallback function is called when the component is removed from the DOM. The function is most useful when we need to remove any event listeners that are connected to a component that is no longer in the DOM.

The attributeChangedCallback function is called when the component’s “observed” attributes get changed.

That’s a lot of theory! Let’s take a look at how web components work by building a simple CRUD-like application.

Getting Started

Tip: Use Bit ( GitHub ) to organize your reusable web components, so you and your team can easily share, develop and reuse them across your apps! It will speed your workflow and help build better modular software:

Reusable web components: choose, compose, build faster

To get started, all we need to do is create a new folder where you can store all the code that you are about to write. You can do this the traditional way, or you can go to a terminal are type the following command:

$ mkdir movieswc

This single command will a new folder called movieswc in your system.

Before moving forward, let me quickly explain the app that we are about to build.

The app is going to be CRUD type where the user can create a list of movies that are going to release in 2019. So the app should contain an array of movies that will be created by the user by entering the movie name and the movie’s release date.

The user will also be able to remove the movie from the list or mark it as “released” or “unreleased”.

Now back to the application. Start by creating the index.html file inside the movieswc folder and write the following code inside it.

<html>
<head>
<script type="module" src="movielist.js"></script>
</head>
<body></body>
</html>

The only thing of notice here is the script tag inside the head of the HTML file. It is pointing a file named movielist.js and we haven’t created it yet. So let’s take care of it and write the following code inside that file.

7neu6na.png!web

That’s a lot of code in one sitting! Let’s take a look at what it all means.

First, we are creating a template element on the document and inserting some HTML inside of it using innerHTML on the template along with some CSS code. You will notice that we have written a selector called  :host , even though we have not used it anywhere in the HTML. host is used to set the styling of the component from the inside.

After that, we are creating an HTML element called MoviesList . Inside of its constructor function, we are attaching the shadowRoot and setting its mode to open. Then, we are cloning our template into the shadowRoot . We have also created the movieList and set it to the currently empty unordered list in our app.

By doing all this, we have created an enclosed DOM tree that will not let any of our styles to leak or get overwritten.

There is one last thing in the above code, which is probably the most important of all of the code that we have written till now. This last line of code registers the MovieList element to the CustomElementRegistry . CustomElementRegistry is used here to define the element and we pass it our desired name of the element (movie-list) along with the actual name of the element (MovieList).

Its very important to know that the name of any of our web components should contain a - in it. If you try to create an element like movielist or movieList , then it won’t work.

With all of that taken care of, we can insert the Web Component into the HTML file’s body.

<body>
<movie-list></movie-list>
<body>

Opening the index.html file on the browser should give you something like this:

3AFbAnY.png!webrUNJryf.png!web

Looks good enough, but we can’t really do anything with it yet. You can click on the button or enter something in the input fields. But nothing else will happen in the browser. In order for our Web Component to communicate with the user or with other things in the application, then we need to create something called Properties.

Properties

To communicate with Web Components, we primarily pass data to it via properties defined on the component. Each property will have a getter and setter which we can use to trigger updates to the component. Let’s write a few of those inside the MovieList Web Component.

First, let's create a new function called _renderMovieList inside the MovieList . This function will help us add just the name of a movie to our currently empty list.

_renderMovieList() {
  this.$movieList.innerHTML = '';
  this._todos.forEach((movie, index) => {
    let $movieItem = document.createElement('li');
    $movieItem.innerHTML = movie.title + `<br>` + movie.date + `<hr>`;
    this.$movieList.appendChild($movieItem);
  });
}

Then, we need to create the setter and getter functions for the movies property as shown below:

set movies(value) {
  this._movies = value;
  this._renderMovieList();
}
get movies() {
  return this._movies;
}

The set function stores the value inside the movies property and calls the _renderMovieList() function. And the get function simply returns the property back to us.

But our app is still incomplete since we have not connected these function to our actual application. Let’s take care of it now. First we need to grab the input fields and the button from the Shadow DOM by writing the following code inside the MovieList ‘s constructor function.

constructor() {
super();
this._shadowRoot = this.attachShadow({'mode': 'open'});
this._shadowRoot.appendChild(template.content.cloneNode(true));
this.$movieList = this._shadowRoot.querySelector('ul');
this.$movieName = this._shadowRoot.getElementById('name');
this.$movieDate = this._shadowRoot.getElementById('date');
this.$button = this._shadowRoot.querySelector('button');
this.$button.addEventListener('click', this._addMovie.bind(this));

}

I am also adding an event listener to the button to listen to any “click” events. When the user clicks on the button, the addMovie function will be triggered, which will first check if the user has written anything in the input fields and then take their values and add them to our list. Write this function inside the MovieList as well.

_addMovie() {
if(this.$movieName.value.length > 0 && this.$movieDate.value.length > 0) {
this._movies.push({title: this.$movieName.value, date: this.$movieDate.value})
this._renderMovieList();
this.$movieName = '';
this.$movieDate = '';
}
}

Tada! If you have reached this point, then your app can successfully render a list of movies like this:

veEvAfQ.jpgeiyIZjj.gif

Congrats! You can now “Get” and “Set” things in your application! But how will you remove the items from the list? That’s where attributes come into play.

Attributes

Attributes provide us with a more convenient way to send data to a web component. Let’s see how to use them by creating a new file called movieItem.js and write the following code in it.

7neu6na.png!web

Just like before, we are creating a new template and inserting some HTML into it. Then we are creating an HTML element named MovieItem and appending the template as a child of the Shadow DOM’s root node.

We are grabbing all the elements from the DOM and attaching a click event listener to the button and the checkbox. We want our app to eventually remove the movie from the list when the user clicks the button and run a line through the item when the user checks the checkbox.

We are then using another lifecycle method called connectedCallback to make sure that the movie’s title and date will be properly rendered into the list.

Then we have created a new function called _renderMovieItem which simply fills the label and p elements with the title and date.

Finally, we are using the attributeChangedCallback lifecycle methods to set the new values of title and date using a switch case statement. As always, remember to register the Web Component at the end of the file. I am giving this Web Component the name of movie-item .

We now need to insert this Web Component inside the movielist.js file. Start by importing it at the very top of the file like this:

import './movieItem.js';

Then we need to make a few changes to the _renderMovieList() function as shown below:

_renderMovieList() {
this.$movieList.innerHTML = '';
this._movies.forEach((movie, index) => {
let $movieItem = document.createElement(`movie-item`);
// $movieItem.innerHTML = movie.title + `<br>` + movie.date + `<hr>`;
$movieItem.setAttribute('title', movie.title);
$movieItem.setAttribute('date', movie.date);

this.$movieList.appendChild($movieItem);
});
}

So instead of creating an li element like before, we are now creating the movie-item component and setting its title and date attributes with the values provided by the user through the input fields.

Ideally, our app should now work as we want it to. If you click on the checkbox input, a line-through should appear across the text, but nothing happens. Let’s take care of it by adding an event listener to the checkbox that listens to for a click event. So go to the constructor method of MovieItem and add the following line of code.

this.$checkbox.addEventListener('click', (event) => {
this.dispatchEvent(new CustomEvent('onToggle', {detail: this.index}));
});
this.toggle = false;
}

Along with the event listener, I have created a toggle value and set it to false . We can use it to create a toggle functionality by creating the _toggleReleased function like this:

_toggleReleased() {
if (this.toggle == false){
this.$title.style.cssText = 'text-decoration: line-through;';
this.$date.style.cssText = 'text-decoration: line-through;';
this.toggle = true;
} else {
this.$title.style.cssText = 'text-decoration: none;';
this.$date.style.cssText = 'text-decoration: none;';
this.toggle = false;
}
}

Now when you click on the checkbox the text will toggle between line-through and normal.

ry6Brir.jpgZRnM3eb.gif

Great! We can now create an entry in the list and toggle between two states. All that is left to do is provide the functionality to delete the entry from the list.

Deleting Entries

Let’s begin by telling the MovieItem component to observe the index attribute.

static get observedAttributes() {
return ['text', 'checked', 'index'];
}

Then go to the attributeChangedCallback lifecycle method and add a case for index and parse it to integer type from string type.

attributeChangedCallback(name, oldValue, newValue) {
switch(name) {
case 'title':
this._title = newValue;
break;
case 'date':
this._date = newValue;
break;
case 'index':
this._index = parseInt(newValue);
break;

}
}

Let’s also write a getter and setter for this index in the MovieItem Web Component.

set index(value) {
this.setAttribute('index', value);
}
get index() {
return this._index;
}

And make this change to the renderMovieList function in the movieList.js file:

_renderMovieList() {
this.$movieList.innerHTML = '';
this._movies.forEach((movie, index) => {
let $movieItem = document.createElement(`movie-item`);
$movieItem.setAttribute('title', movie.title);
$movieItem.setAttribute('date', movie.date);
$movieItem.setAttribute('index', index);
$movieItem.addEventListener('onRemove', this._removeMovie.bind(this));

this.$movieList.appendChild($movieItem);
});
}

We are setting the index attribute of the list entry to the value of index . Then we are add an event listener to it that listens for the onRemove custom event and triggers the _removeMovie function. Let's create that function in the MovieList component.

_removeMovie(event) {
this._movies.splice(event.detail, 1);
this._renderMovieList();
}

Reload your browser, and you should be able to delete things from the Movie List!

YN32uma.jpgbaEryeN.gif

That’s A Wrap!

Thank you for reading this extremely long post on Web Components! I hope it helped you understand Web Components and how to use them in your next project. There are lot of code and theory in this post, and if you did get lost somewhere then you take look at the entire source code for this post here:

But this is not the only way to create Web Components. For instance, Google maintains a polyfill called webcomponentsjs that can help implement web components in a simpler way.

There are also many other types of ways that you can create Web Components. In my next post, I will show how to create Web Components with lit-html! So stick around and feel free to comment here if you have any thoughts to share with me! Cheers :beer:


About Joyk


Aggregate valuable and interesting links.
Joyk means Joy of geeK