problem mounting with `el` option after unmounted
karladler opened this issue · comments
When I add the el
option the first time the instance is correctly rendered into the selected element.
But since vue replaces this element there is a problem mounting it again after unmounted, because the desired element does not exists anymore on the DOM.
The provided element merely serves as a mounting point. Unlike in Vue 1.x, the mounted element will be replaced with Vue-generated DOM in all cases. It is therefore not recommended to mount the root instance to or .
In my case I just want to layout the positions of the single applications.
<div id="all-apps">
<div id="navbar"></div>
<div id="first" style="width: 50%;">
<div></div>
</div>
<div id="second" style="width: 50%;">
<div></div>
</div>
</div>
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
render: (h) => h(App),
router,
el: '#first > div'
},
});
The <div class="single-spa-container">
shown below is how this avoided when the el
option is not provided:
single-spa-vue/src/single-spa-vue.js
Lines 64 to 71 in bd25b1f
We could perhaps try to do something similar when #el
is provided. Do you have any ideas about how to solve this?
Hi @karladler ! As you want to make a custom layout I think this is vue matter and not single-spa's.
I had to do a custom configuration in my root application and my single-spa-vue app for keep the anchor all time for un/mount so many times I want.
You can see this strategy in next files:
The
<div class="single-spa-container">
shown below is how this avoided when theel
option is not provided:single-spa-vue/src/single-spa-vue.js
Lines 64 to 71 in bd25b1f
We could perhaps try to do something similar when
#el
is provided. Do you have any ideas about how to solve this?
Spent quite a lot time trying to understand why error happens on unmount stage, when parcel is mounted by single-spa-react/parcel
. I am not common with Vue and did not know that it replaces the element instead of appending, which causes React to crash.
Ended up with similar solution:
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
render: (h) => h(App)
}
});
export const bootstrap = vueLifecycles.bootstrap;
export const mount = (props) => {
if (props.domElement && props.append) {
const el = document.createElement('div');
props.domElement.appendChild(el);
props.domElement = el;
}
return vueLifecycles.mount(props);
};
export const unmount = vueLifecycles.unmount;
I'm very open to figuring out a better way of handling these situations - perhaps automatically appending the wrapper container when the #el
option is provided.
Hello, we have a lot of components, so we didn't want to add loads of empty wrapper divs in our html-template. We worked around the issue by creating a new component-div with a random id during the mount phase, which gets appended to our main-wrapper. The newly crated component-div gets replaced with the single-spa app by Vue.
Unfortunately, we can't access the newly created element for removal after it was replaced with the Vue app. So as a workaround, we empty the main-wrapper manually on unmount. The same logic is then repeated for the new app. If there was a way to just cleanly remove the newly created component-div without having to remove all siblings this would be great.
HTML
<body class="wrapper">
<div class="sidebar-wrapper">
<div id="sidebar"></div>
</div>
<main class="main-wrapper" id="main-wrapper"></main>
</body>
TS
...
import nanoid from 'nanoid'
const createComponentRootElement = (rootId: string): void => {
const spaWrapper: HTMLElement = document.getElementById(rootId) || document.body
const newComponent: HTMLElement = Object.assign(
document.createElement('div'), {
id: componentId
}
)
spaWrapper.appendChild(newComponent)
}
const clearComponentRootElement = (rootId: string): void => {
const spaWrapper: HTMLElement = document.getElementById(rootId) || document.body
while (spaWrapper.firstChild) {
spaWrapper.removeChild(spaWrapper.firstChild)
}
}
const rootClass: string = 'main-wrapper'
const componentId: string = 'id' + nanoid().toLocaleLowerCase()
const vueLifecycles = singleSpaVue({
Vue,
appOptions: {
render: (h:any) => h(ComponentName),
router,
el: `#${componentId}`
}
})
export const bootstrap = [
vueLifecycles.bootstrap
]
export const mount = [
() => {
createComponentRootElement(rootClass)
return Promise.resolve()
},
vueLifecycles.mount
]
export const unmount = [
vueLifecycles.unmount,
() => {
clearComponentRootElement(rootClass)
return Promise.resolve()
}
]
I just submitted a fix in #34
This is now supported in https://github.com/single-spa/single-spa-vue/releases/tag/v1.8.0