I created two web-components and nested one of them into the other.
Both of them have its constructor
. The problem that I have is that, I have no control on running the sequence of the constructor
s.
Is there any way which I can set this out?
Here's my code:
child web component:
(function () {
const template = document.createElement('template');
template.innerHTML = `<div>WC1</div>`;
class WC1Component extends HTMLElement {
constructor() {
super();
console.log('WC1: constructor()');
var me = this;
me._shadowRoot = this.attachShadow({ 'mode': 'open' });
me._shadowRoot.appendChild(template.content.cloneNode(true));
}
connectedCallback() {
console.log('WC1: connectedCallback');
}
test() {
console.log('test:wc1');
}
}
window.customElements.define('wc-one', WC1Component);
}());
parent web component:
(function () {
const template = document.createElement('template');
template.innerHTML = `
<wc-one id="wc1"></wc-one>
<div>WC2</div>
`;
class WC2Component extends HTMLElement {
constructor() {
super();
console.log('WC2: constructor()');
var me = this;
me._shadowRoot = this.attachShadow({ 'mode': 'open' });
me._shadowRoot.appendChild(template.content.cloneNode(true));
me._wc1 = me._shadowRoot.querySelector('#wc1');
}
connectedCallback() {
console.log('WC2: connectedCallback');
this._wc1.test(); // <-- Error: test is undefined!
}
}
window.customElements.define('wc-two', WC2Component);
}());
index.html
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Test Web Component</title>
</head>
<body>
<script src="wc1.js"></script>
<script src="wc2.js"></script>
<wc-two></wc-two>
<script>
</script>
</body>
</html>
The result:
WC2: constructor()
WC2: connectedCallback
Uncaught TypeError: this._wc1.test is not a function
WC1: constructor()
WC1: connectedCallback
Actually you do have control on the running sequence of the constructors.
Since <wc-one>
is created by <wc-two>
the WC2 constructor will always be called before WC1.
The error is due to the fact that when you try invoke it, the inner component (WC1) is not yet added to the DOM.
You could listen for the DOMContentLoaded
event to be sure element is OK, or the load
event on window
, or implement the window.onload
handler. @elanz-nasiri is aslo working.
DOMContentLoaded
will be fired first.
There're alternate solution but they are more difficult to implement.
window.customElements.define('wc-one', class extends HTMLElement {
constructor() {
super()
this.attachShadow({ 'mode': 'open' }).innerHTML = `<div>WC1</div>`
}
test(source) {
console.log('test:wc1', source)
}
} )
window.customElements.define('wc-two', class extends HTMLElement {
constructor() {
super()
this._shadowRoot = this.attachShadow({ 'mode': 'open' })
this._shadowRoot.innerHTML = `<wc-one id="wc1"></wc-one><div>WC2</div>`
this._wc1 = this._shadowRoot.querySelector('#wc1');
}
connectedCallback() {
setTimeout( ()=>this._wc1.test('setTimout') )
document.addEventListener( 'DOMContentLoaded', ()=>this._wc1.test('DOMContentLoaded') )
window.onload = ()=>this._wc1.test('window.onload')
window.addEventListener( 'load', ()=>this._wc1.test('load') )
}
} )
<wc-two></wc-two>