I have a custom web component treez-tab-folder
. As the name suggests it represents a tab folder. Here is a jsfiddle and some image to demonstrate its usage (might require Chrome browser to work correctly):
https://jsfiddle.net/fg4dL2rx/
I would like to use vue.js to bind the properties of a JavaScript object to some attributes of the tab folder. If I use a div
inside a treez-tab
as target element for the Vue instance (el: '#firstContent'
), the binding works as expected (also see above jsfiddle):
<body>
<treez-tab-folder id="tabFolder">
<treez-tab title="First tab">
<div id='firstContent'>{{message}}</div>
</treez-tab>
<treez-tab title="Second tab">
<div>Second tab content</div>
</treez-tab>
</treez-tab-folder>
<script>
new Vue({
el: '#firstContent',
data: {
message: 'First tab content'
}
});
</script>
</body>
However, if I try to use the custom element treez-tab-folder
directly as target element for the Vue instance (el: '#tabFolder'
), the jsfiddle example stops working:
https://jsfiddle.net/etomuf8v/
Now the tab headers are constructed twice and the tab content seems to be missing:
<body>
<treez-tab-folder id="tabFolder">
<treez-tab title="First tab">
<div id='firstContent'>{{message}}</div>
</treez-tab>
<treez-tab title="Second tab">
<div>Second tab content</div>
</treez-tab>
</treez-tab-folder>
<script>
new Vue({
el: '#tabFolder',
data: {
message: 'First tab content'
}
});
</script>
</body>
=> How can I fix my web component? or
=> Does vues.js forbit to use custom elements as target?
I thought that the disposal of my components might not work correctly when vue.js is replacing/refreshing the dom. Therefore I tried to implement disconnectedCallback
to kill remaining elements. That did not help.
Here are my custom tab elements:
<script>
class TreezTabFolderHeader extends HTMLElement {}
window.customElements.define('treez-tab-folder-header', TreezTabFolderHeader);
class TreezTabHeader extends HTMLElement {}
window.customElements.define('treez-tab-header', TreezTabHeader);
class TreezTabFolder extends HTMLElement {
constructor(){
super();
this.tabFolderHeader=undefined;
}
connectedCallback() {
if(!this.tabFolderHeader){
this.style.display='block';
this.tabFolderHeader = document.createElement('treez-tab-folder-header');
this.insertBefore(this.tabFolderHeader, this.firstChild);
}
}
disconnectedCallback(){
while (this.firstChild) {
this.removeChild(this.firstChild);
}
}
}
window.customElements.define('treez-tab-folder', TreezTabFolder);
class TreezTab extends HTMLElement {
constructor(){
super();
console.log('tab constructor');
this.tabHeader=undefined;
}
static get observedAttributes() {
return ['title'];
}
connectedCallback() {
console.log('connected callback');
if(!this.tabHeader){
var headers = this.parentElement.children[0];
this.tabHeader = this.createTabHeader(this.parentElement);
this.tabHeader.innerText = this.title;
headers.appendChild(this.tabHeader);
this.showFirstTab(this.parentElement);
}
}
disconnectedCallback(){
console.log('disconnected callback');
while (this.firstChild) {
this.removeChild(this.firstChild);
}
}
adoptedCallback(){
console.log('adopted callback');
}
attributeChangedCallback(attr, oldValue, newValue) {
if(attr==='title' && this.tabHeader){
this.tabHeader.innerText= newValue;
}
}
createTabHeader(tabs){
var tabHeader = document.createElement('treez-tab-header');
tabHeader.onclick=()=>{
var tabHeaders = tabs.children[0].children;
for(var index=1;index<tabs.children.length;index++){
var tab = tabs.children[index];
tab.style.display='none';
var tabHeader = tabHeaders[index-1];
tabHeader.classList.remove('selected')
}
this.style.display='block';
this.tabHeader.classList.add('selected')
};
return tabHeader;
}
showFirstTab(tabs){
var firstHeader = tabs.children[0].children[0];
firstHeader.classList.add('selected')
tabs.children[1].style.display="block"
for(var index=2;index<tabs.children.length;index++){
tabs.children[index].style.display="none";
}
}
}
window.customElements.define('treez-tab', TreezTab);
</script>
and the css file
treez-tab-folder {
background-color:#f2f2f2;
width:100%;
height:100%;
padding-top:2px;
font-family: Arial,sans-serif;
font-size: 12px;
}
treez-tab-folder-header {
margin-left:-3px;
color: #777777;
}
treez-tab-header {
background-color:#f2f2f2;
display:inline-block;
margin-left: 2px;
padding:8px;
padding-top:1px;
padding-bottom:3px;
border: 1px solid;
border-color:#cccccc;
box-shadow: 2px -2px 2px -2px rgba(0,0,0,0.2);
transform: translate(0px, 1px);
}
treez-tab-header:hover {
background-color:#e1e1e1;
}
treez-tab-header.selected {
border-bottom: none;
background-color:#e1e1e1;
transform: translate(0px, 2px);
padding-top:2px;
}
treez-tab {
background-color:#e1e1e1;
border-top: 1px solid;
border-color:#cccccc;
border-bottom: none;
box-shadow: 2px -2px 2px -2px rgba(0,0,0,0.2);
height:100%;
vertical-alignment:bottom;
}
See Question&Answers more detail:
os