I'm creating a custom element and looking for a good way to pass in an object representing its initial state. I'm thinking my html would look something like this:
<my-component id="myComponent1"></my-component>
<script>
const myComponent1=document.getElementById("myComponent1");
myComponent1.stateObj={ orientation:"inverted", splines:5, clowns:true }
</script>
And my js files will include:
class MyComponent extends HTMLElement {
stateObj;
constructor(){ super(); }
connectedCallback(){
// some init stuff
}
get stateObj(){
return this.stateObj;
}
set stateObj(stateObj){
this.stateObj = stateObj;
}
}
customElements.define('my-component',MyComponent);
In testing this appears to work, and I like that the initial state is defined in my html document right next to the component. But can I be sure the component will always be ready to receive a property assignment from a script tag parsed immediately after it?
Like any HTML element, just be sure it exists in the DOM before you do something with the element.
Some notes:
a constructor
with only a super()
is redundant, as the parent class constructor
is called by default when it is doesn't exist
Your this.stateObj = stateObj
overwrites the get stateObj()
defining the Web Component with an anonymous class is faster, shorter
Unless you want to re-use a Web Component Class multiple times (99,9999% not required)
this._stateObj = obj
passes a reference to an existing Object, you are not creating a new Object... its JavaScript!
this.stateObj
returns undefined in the connectedCallback
because it is not set yet
<my-component id="myComponent1"></my-component>
<script>
customElements.define('my-component', class extends HTMLElement {
connectedCallback() {
console.log(this.id, "connected", this.stateObj)
}
get stateObj() {
return this._stateObj;
}
set stateObj(obj) {
console.log(obj);
this.innerHTML = JSON.stringify(obj);
this._stateObj = obj;
}
});
</script>
<script>
const myComponent1 = document.getElementById("myComponent1");
if (myComponent1)
myComponent1.stateObj = {
orientation: "inverted",
splines: 5,
clowns: true
}
</script>
If you use an attribute to pass an Object, it needs to be a correctlty quoted String.
https://javascript.info/json
The Web Component then converts the String to an Object
This a 30+ years old HTML limitation because attribute values are always a String
Note the difference between setting state (first example above) and loading state (second example below)
No need to check if <my-component>
exists in the DOM,
this Web Component is 100% in control
<script>
customElements.define('my-component', class extends HTMLElement {
connectedCallback() {
this.stateObj = this.getAttribute("json");
console.log(this.id,"connected",this._stateObj);
this.innerHTML = JSON.stringify(this.stateObj)
}
get stateObj() {
return this._stateObj || {}
}
set stateObj( obj ) {
if( typeof obj == "string" ){
this._stateObj = JSON.parse(obj)
} else {
this._stateObj = obj;
}
}
});
</script>
<my-component id="load_with_attribute"
json='{"orientation":"inverted", "splines":5, "clowns":true}'></my-component>
<script>
customElements.define('my-component', class extends HTMLElement {
connectedCallback() {
this.stateObj = {"orientation":"inverted", "splines":5, "clowns":true};
}
get stateObj() {
return this.stateObj // !!!
}
set stateObj( obj ) {
this.stateObj = obj; // !!!
}
});
</script>
<my-component></my-component>
JSWC