CWCO logo

v1.7.5

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:

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 component onUpdate to be called.

To reference getters inside the template you must use the this keyword as they are not seen as normal properties.