Properties
Properties are simply public class properties that WebComponent
watches for updates.
class TodoItem extends WebComponent {
// normal class properties
title = 'untitled';
description = '';
status = 'in-progress';
get template() {
return `
<div class="todo-item">
<h3>{title}</h3>
<p>{description}</p>
<p><strong>Status</strong> {status}</p>
</div>
`;
}
}
Property Update
Property can be updated inside the class or directly on the class element instance.
const todo = new TodoItem();
todo.title = 'My Todo';
document.body.appendChild(todo);
Whenever there is a property update the component will update the DOM. The example below will update the count whenever the increment and decrement buttons are clicked.
class CounterWidget extends WebComponent {
// properties
count = 0;
get template() {
return `
{count}
<button type="button" onclick="updateCount(this.count - 1)">
decrement</button>
<button type="button" onclick="updateCount(this.count + 1)">
increment</button>
`;
}
updateCount(newCount) {
this.count = newCount;
}
}
CounterWidget.register();
document.body.appendChild(new CounterWidget())
Deep Updates
The component can even detect deep object changes.
If we change the count to be an object with the value
property, the component will update the DOM when we change
that property.
Any property you declare on the class, including all the attributes, will be deeply watched.
class CounterWidget extends WebComponent {
// properties
count = {value: 0};
get template() {
return `
{count.value}
<button type="button" onclick="updateCount(this.count.value - 1)">
decrement
</button>
<button type="button" onclick="updateCount(this.count.value + 1)">
increment
</button>
`;
}
updateCount(newCount) {
this.count.value = newCount;
}
}
CounterWidget.register();
document.body.appendChild(new CounterWidget())
This is what makes this library a truly reactive. It does this by using Proxy behind the scenes.
So, updates are triggered by:
- Any property re-assignment;
- Deep updates on observable properties.
Observable properties
Class properties can have any value. You can set an Element instance, a CanvasRenderingContext2D instance, a custom class, etc.
These are not valid data and whenever they change will not trigger the component to update. They are static data values which you can still reference inside the template.
CWCO considers observable properties to be of type:
Private Property
Private properties are private to anything outside the class. They are perfect when you want to set data that should only be accessed or changed from inside the class.
One thing to know is that they do not trigger component update on changes.
If a property is not used in the template, it SHOULD be private. Use them for internal calculations to further optimize your component.
class CountDown extends WebComponent {
static observedAttributes = [
'count'
];
#timer = 0;
get template() {
return '{count}';
}
onMount() {
this.#timer = setInterval(() => {
if(this.count > 0) {
this.count -= 1;
} else {
clearInterval(this.#timer)
}
}, 1000)
}
onDestroy() {
clearInterval(this.#timer)
}
}
The above example shows that since #timer
is not used in template, and it is only an internal
piece of data, it is just private. This is a best practice to follow which will avoid
un-necessary component updates.
Getters and Setters
Getters and setters are not considered normal properties, and they will not trigger component updates. If you want to use them you can combine your setters with forceUpdate
class CountUpButton extends WebComponent {
#count = 0;
get count() {
return this.#count;
}
set count(val) {
this.#count = val;
this.forceUpdate();
}
get template() {
return '<button type="button">{this.count}</button>'
}
}
Note:forceUpdate
will not trigger componentonUpdate
to be called.
To reference getters inside the template you must use the this keyword as they are not seen as normal properties.