if i create two custom elements and create one of them inside the other one, the attributes are not working on the new childs of the first element.
class Bar extends HTMLElement {
constructor() {
super();
const val = this.getAttribute('val') || 'no value';
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
shadow.appendChild(wrapper);
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
const b = document.createElement('x-bar');
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
im expecting that my output would be
value #0
value #1
value #2
as i have set the attr like this b.setAttribute('val', value #${i});
but im getting 3x no value
Any inputs on why that is? and/or how to fix it, Thanks!
You're not setting the attribute until after the constructor has already been called; note the logging:
class Bar extends HTMLElement {
constructor() {
super();
const val = this.getAttribute('val') || 'no value';
console.log("In constructor, val = " + val);
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
shadow.appendChild(wrapper);
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
console.log("Constructing...");
const b = document.createElement('x-bar');
console.log("Setting attribute");
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
You'll need to move your rendering logic out of the constructor so you can take attributes set post-construction into account. Perhaps by overriding setAttribute
:
class Bar extends HTMLElement {
constructor() {
super();
const shadow = this.attachShadow({mode: 'open'});
this.wrapper = document.createElement('div');
this.render(); // Though calling methods from the constructor isn't ideal
shadow.appendChild(this.wrapper);
}
setAttribute(name, value) {
super.setAttribute(name, value);
if (name === "val") {
this.render();
}
}
render() {
const val = this.getAttribute('val') || 'no value';
this.wrapper.innerHTML = `
<div class='bar'>
<span>${val}</span>
</div>
`;
}
}
customElements.define('x-bar', Bar);
class Foo extends HTMLElement {
constructor() {
super();
const loop = this.getAttribute('loop') || 10;
const shadow = this.attachShadow({mode: 'open'});
const wrapper = document.createElement('div');
for(let i=0; i<loop; i++){
console.log("Constructing...");
const b = document.createElement('x-bar');
console.log("Setting attribute");
b.setAttribute('val', `value #${i}`);
wrapper.appendChild(b);
}
shadow.appendChild(wrapper);
}
}
customElements.define('x-foo', Foo);
<x-foo loop='3'></x-foo>
Calling methods from the constructor isn't ideal, though, you may want to fiddle with that a bit