Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Page Refresh causing items to load in body element instead of designated element. #330

Open
TheColorRed opened this issue Dec 28, 2020 · 3 comments

Comments

@TheColorRed
Copy link

TheColorRed commented Dec 28, 2020

Demonstration

  1. Load root /
  2. Navigate to page that triggers the microservice to load
  3. 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:

image
image

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

image
image

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.

@TheColorRed
Copy link
Author

TheColorRed commented Dec 28, 2020

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?

@TheColorRed
Copy link
Author

TheColorRed commented Dec 28, 2020

If there currently isn't a way to do this, maybe something like this would work?

  if (ivyEnabled()) {
    const domElementGetter = chooseDomElementGetter(options, props);
    getContainerElement(domElementGetter).then(domElement => {
      while (domElement.firstChild) domElement.removeChild(domElement.firstChild);
    }).catch(err => throw new Error(err));
  }

function getContainerElement(domElementGetter: DomElementGetter): never | HTMLElement {

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()
  })
}

@daniloesk
Copy link

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.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants