Attributes and properties

We have been playing around with attributes since the first chapter. And we did get a brief overview of properties and how they can work along with state management to provide a more complete Web Component.

But what is the exact difference between the two? If you are a frontend developer, you must have created a form in your career. We will be looking at an example of an <input> tag:

<input type="text" value="default value" />

If you look at it carefully, we have an attribute called value giving it some default value. So if you want to get the value of this <input> tag, you can get it by using the following code:

document.querySelector('input').getAttribute('value');

So, you are directly referencing the attribute for this <input> tag to get the value. But there is another way in which you can get this value. And that is as follows:

document.querySelector('input').value;

This time, we are grabbing the value from the property value of the <input> tag.

Now the question is, what is the difference? The difference is whether to show it in an attribute or not. There will always be a value that you might not want to show to the HTML code. It may be too long, such as a playlist in a music player Web Component, where the list contains a JSON-style data structure of song names and URLs, or a tax ID number like SSN in a tax registration component, where the data is too sensitive to be put as an attribute. 

Let's try to look at this with the help of an example. Let's say we have a Web Component called <student-list> where we have an input field that is used to enter student names and a button that lets you add students to the student list. This is what the component looks like:

constructor() {

// We are not even going to touch this.
super();

// Initially, the list is empty
this._list = [];

// lets create our shadow root
this.shadowObj = this.attachShadow({mode: 'open'});
this.render();
}

Here, we are managing the student list inside the _list variable. The rest is the same as usual:

render() {
this.shadowObj.innerHTML = this.getTemplate();
}

getTemplate() {
return `
<div class="student-list__form">
<input type="text" name="student-name"
class="student-list__input"
placeholder="Enter Student Name here"/>
<button class="js-addButton student-list__add-button">Add Student</button>
</div>
<div class="student-list__student-container">
<div class="student-list__student-container-heading">Student List</div>
<div class="student-list__student-list">
${this.getStudents()}
</div>
</div>
${this.getStyle()}
`;
}

As you can see, we have an input field, a button, and a div student-list__student-list to put our students in the form of a list:

getStudents() {
return this._list.map((item, num) => {
return `<div class="student-list__student">${num + 1}. ${item}</div>`;
}).join('');
}

This getStudents() method shows the students by running over the _list variable that we declared in the constructor() method. Let's take a look at our styles before we move on to other sections of this Web Component:

getStyle() {
return `
<style>
:host {
display: block;
}
.student-list__form {
display: flex;
align-items: center;
}
.student-list__input {
height: 44px;
margin: 0 25px;
width: 300px;
border-radius: 10px;
border-width: 1px;
font-size: 18px;
padding: 0 20px;
}
.student-list__add-button {
height: 50px;
width: 200px;
border-radius: 5px;
display: inline-block;
border: 1px solid #cac6c6;
}
.student-list__student-container {
margin-top: 50px;
border-top: 1px solid black;
padding-top: 50px;
font-size: 25px;
}
.student-list__student-container-heading {
margin-bottom: 20px;
}
.student-list__student {
padding: 10px;
margin-bottom: 10px;
border-bottom: 1px solid #bfbfbf;
}
</style>
`;
}

It's basic CSS, nothing complex. Now, let's add an event listener to our button so that it can add the students to our _list variable:

connectedCallback() {

// what should happen when the button is clicked
this.shadowObj.querySelector('.js-addButton')
.addEventListener("click", (e) => {
this.handleAdd(e);
});
}

handleAdd() {
let value = this.shadowObj.querySelector('input[name=student-name]').value;
this._list.push(value);
this.renderList();
}

renderList() {
this.shadowObj.querySelector('.student-list__student-list').innerHTML
= this.getStudents();
}

Here, we are adding a click event listener to the button .js-addButton. When a user clicks on the button, it grabs the value of the input field, and pushes it to our _list variable. After that, we are simply re-rending the list; in other words, rather than setting the inner HTML of our component again from scratch, we are simply changing the HTML of the section that needs to be updated.

But what if the user wants to see the student list, or grab it from the component? For this, let's add a property students for our user:

set students (value) {
this._list = value;
this.renderList();
}

get students (){
return this._list;
}

This way, the user can get the student list by using the following code:

document.querySelector('student-list').students;

This would give the user all the students that have been added in the form of an array:

But now you must be thinking, what if we were to make this available in the attributes? The answer is yes, we can do that. We can update our handleAdd() method to something like this:

handleAdd() {
let value = this.shadowObj.querySelector('input[name=student-name]').value;
this._list.push(value);
this.setAttribute("students", this._list);
this.renderList();
}

This will make the list available in an attribute called students. But this is what the attribute will look like:

Do you really want your users to manually parse a string to get an array? What if this data was a little bit more complex? Would the user know what needs to be parsed? In order to solve these complications, we use properties. 

I hope this use case will help you decide what to put in properties and what to put in attributes.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.188.121