Page Refresh causing items to load in body element instead of designated element.
Demonstration
- Load root
/ - Navigate to page that triggers the microservice to load
- Reload the page F5
Expected Behavior
When reloading a page that has a microservice the service should load in the same spot.
Actual Behavior
When I navigate to a page single spa loads the microservice correctly:

However, if I reload this page (F5), the microservice loads in the wrong location:

Code
<div class="app-container">
<mat-toolbar color="primary">
<mat-toolbar-row fxLayoutAlign="space-between center">
<!-- Top bar buttons -->
</mat-toolbar-row>
</mat-toolbar>
<mat-sidenav-container style="flex: 1;" [hasBackdrop]="false">
<mat-sidenav #drawer mode="side" [opened]="true">
<!-- Sidebar buttons -->
</mat-sidenav>
<mat-sidenav-content>
<div class="router-container">
<div>
<router-outlet></router-outlet>
</div>
<!--
Single Spa Microproducts
-->
<div *ngIf="isLoggedIn">
<div id="single-spa-application:file-manager"></div>
<div id="single-spa-application:resume-builder"></div>
</div>
</div>
</mat-sidenav-content>
</mat-sidenav-container>
</div>
I am registering the apps like this:
const HOST = environment.production ? 'https://promote.red5js.com' : 'http://127.0.0.1:5500'
const APPS = `${HOST}/apps`
function registerApp(name: string, activeWhen: string | string[], location: string) {
registerApplication({
name,
activeWhen: [].concat(activeWhen),
customProps: {
globalEventBus: new GlobalEventBus(),
globalEventStore: new GlobalEventStore()
},
app: () => System.import(location)
} as RegisterApplicationConfig)
}
// Register the core application navigation, etc.
registerApp('core', '/', `${HOST}/core/main.js`)
// Register all the other applications
registerApp('file-manager', '/file-manager', `${APPS}/file-manager.js`)
registerApp('resume-builder', '/resume-builder', `${APPS}/resume-builder.js`)
start({ urlRerouteOnly: false })
When refreshing the page the item is created in the body element, instead of using the existing div element.
Note: the whole project (including microservices) is using Angular 11.
I have added the following to my main.single-spa.ts file:
const lifecycles = singleSpaAngular({
domElementGetter: () => {
return document.getElementById('file-manager')
}
})
I then renamed the div (seen in the OP)
This works, however when reloading the page, it cannot find the element.
Uncaught Error: domElementGetter did not return a valid dom element
How would I get a dom element that is loaded lazily?
If there currently isn't a way to do this, maybe something like this would work?
https://github.com/single-spa/single-spa-angular/blob/e582c4a356d7e23a994b54dc1ff20f21c4896866/libs/single-spa-angular/internals/src/dom.ts#L7
if (ivyEnabled()) {
const domElementGetter = chooseDomElementGetter(options, props);
getContainerElement(domElementGetter).then(domElement => {
while (domElement.firstChild) domElement.removeChild(domElement.firstChild);
}).catch(err => throw new Error(err));
}
https://github.com/single-spa/single-spa-angular/blob/e582c4a356d7e23a994b54dc1ff20f21c4896866/libs/single-spa-angular/internals/src/dom.ts#L36
function getContainerElement(domElementGetter: DomElementGetter): Promise<never | HTMLElement> {
return new Promise<HTMLElement>((resolve, reject) => {
let element: HTMLElement
let count = 0
const max = 10
function watchDom() {
element = domElementGetter()
if (!element) {
if (count < max) {
count++
setTimeout(watchDom, 100)
} else {
if (!element) {
reject('domElementGetter did not return a valid dom element')
}
}
} else {
resolve(element)
}
}
watchDom()
})
}
Sorry for not having time to take a deep look into your problem, but by my quick peek I understand you are using an Angular app as root-config instead of an agnostic HTML/JavaScript one. And then I am betting on tag name conflicts: app-root and router-outlet tags both in root-config app and Angular microservice.