Creating a Web Component with Data-Binding Capability

by valkoivo

HTML

<!DOCTYPE html>
<html>
<body>

	<my-provider id="provider_id"></my-provider>
	<my-consumer id="consumer_id" provider_id="provider_id" provider_event="my-provider-data-changed"></my-consumer>

</body>
<script>

</script>
</html>

JavaScript

class MyProvider extends HTMLElement {
	constructor(){
		super();
		this._data = {
			value: 'Some initial value'
		};
	}
	_createEvent(){
		this._event = new CustomEvent('my-provider-data-changed');
	}
	_announceDataReady(){
		this.dispatchEvent(this._event);
	}
	_announceDataChanged(){
		this.dispatchEvent(this._event);
	}
	connectedCallback() {
		this._createEvent();
		this._announceDataReady();
	}
	changeData(new_value){
		this._data.value = new_value;
		this._announceDataChanged();
	}
}

const CustomInterfaceExposable = Sup => class extends Sup {
	retrieveDataObject(){
		return this._data;
	}
}

class MyCustomProvider extends CustomInterfaceExposable(MyProvider) {
}

const ProviderConnectable = Sup => class extends Sup {
	constructor(){
		super();
		this.provider_id = this.getAttribute("provider_id");
		this.provider_event = this.getAttribute("provider_event");
	}
	connectedCallback() {
		this._provider = this._identifyProvider();
		this._onProviderIdentified();
		this._subscribeToProviderEvent();
	}
	disconnectedCallback() {
		this._unsubscribeFromProviderEvent();
	}
	_identifyProvider(provider_id){
		return document.getElementById(
			this.provider_id
		);
	}
	_onProviderIdentified(){
		// Do something to initialize
	}
	_subscribeToProviderEvent(){
		this._onProviderDataChangedBinded = this._onProviderDataChanged.bind(this);
		this._provider.addEventListener(
			this.provider_event,
			this._onProviderDataChangedBinded
		);
	}
	_isProviderReady(){
		return this._provider && (typeof(this._provider.connectedCallback) === 'function');
	}
	_onProviderDataChanged(event){
		// Do something with changed data
	}
	_unsubscribeFromProviderEvent(){
		this._provider.removeEventListener(
			this.provider_event,
			this._onProviderDataChangedBinded
		);
	}
}

const CustomInterfaceConsumable = Sup => class extends Sup {
	_retrieveProviderDataObject(provider){
		return provider.retrieveDataObject();
	}
}

class MyConsumer extends...