angular / preboot

Coordinate transfer of state from server to client view for isomorphic/universal JavaScript web applications
MIT License
382 stars 51 forks source link

server app instance not removed from DOM after client bootstraps app #111

Open mohamedaarab1994 opened 4 years ago

mohamedaarab1994 commented 4 years ago

Note: for support questions, please use the Universal Slack Channcel or https://gitter.im/angular/universal

https://i.imgur.com/S8n4ctp.png

a-syde commented 4 years ago

@mohamedaarab1994 Have you got any updates on this? Angular doesn't bootstrap client side at all, therefore there is no JS code at all. Only links work. Preboot works as it should when you run: "dev:ssr": "ng run frontend:serve-ssr" It is really frustrating :disappointed: image

akshata456 commented 3 years ago

@CaerusKaru any update on this? I'm facing the same issue in angular 9

shyamal890 commented 3 years ago

I am facing the same issue, can you please update us on this please.

ocombe commented 3 years ago

I had the same issue until I realized that appId and appRoot need to be the same:

In my AppModule:

BrowserModule.withServerTransition({ appId: 'app-root' }),
PrebootModule.withConfig({ appRoot: 'app-root' })

After that, everything worked like a charm.

akshata456 commented 3 years ago

I had the same issue until I realized that appId and appRoot need to be the same:

In my AppModule:

BrowserModule.withServerTransition({ appId: 'app-root' }),
PrebootModule.withConfig({ appRoot: 'app-root' })

After that, everything worked like a charm.

It works for me .Thanks.

ocombe commented 3 years ago

Ok another reason why this might fail: preboot uses the observable appRef.isStable, which might never get stable because according to the documentation:

the application will never be stable if you start any kind of recurrent asynchronous task when the application starts (for example for a polling process, started with a setInterval, a setTimeout or using RxJS operators like interval);

These asynchronous tasks can be in your code (setTimeout, setInterval, or interval operator), or in any of your external libraries... making it very hard to debug.

A solution is to manually do the cleanup:

This will make the replay of events, and then the cleanup of the server side application. You will be responsible for making sure that your app is loaded, otherwise you'll see a nice FOC when it switches from one app to the other... If you're not replaying events, you can directly call this.eventReplayer.cleanup();

To help you detect pending async events such as setInterval, you can use the following code in your main.ts file:

document.addEventListener('DOMContentLoaded', () => {
  platformBrowserDynamic()
    .bootstrapModule(AppModule)
    .then(moduleInstance => {
      // Ensure Angular destroys itself on hot reloads.
      if (window['ngRef']) {
        window['ngRef'].destroy();
      }
      window['ngRef'] = moduleInstance;
      const ngZone = moduleInstance.injector.get(NgZone);
      setInterval(() => {
        const taskTrackingZone = (<any>ngZone)._inner.getZoneWith('TaskTrackingZone');
        if (!taskTrackingZone) {
          throw new Error(
            `'TaskTrackingZone' zone not found! Have you loaded 'node_modules/zone.js/dist/task-tracking.js'?`
          );
        }
        let tasks: any[] = taskTrackingZone._properties.TaskTrackingZone.getTasksFor('macroTask');
        tasks = clone(tasks);
        if (size(tasks) > 0) {
          console.log('ZONE pending tasks', tasks);
        }
      }, 1000);
      // Otherwise, log the boot error
    })
    .catch((error: string) => {
      /* tslint:disable-next-line */
      console.error(error);
    });
});

And add import 'zone.js/dist/task-tracking'; in your polyfill.ts file