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

Lazy Routes reload whole App #76

Open
topaxi opened this issue May 22, 2018 · 22 comments
Open

Lazy Routes reload whole App #76

topaxi opened this issue May 22, 2018 · 22 comments

Comments

@topaxi
Copy link

topaxi commented May 22, 2018

When I have configured lazy routes in my App, triggering a change hot-reloads the whole App.

Haven't found a way to fix this, but I have found someone with the same problem on Stackoverflow: https://stackoverflow.com/questions/48236160/angular-cli-hmr-with-lazy-loaded-routes-hot-reloads-the-whole-thing

@maxencefrenette
Copy link

Just confirming that I'm getting the same issue, which makes hmr completely pointless as all my business features are lazy-loaded.

@ronsc
Copy link

ronsc commented Aug 22, 2018

same issue

@adzza24
Copy link

adzza24 commented Oct 8, 2018

I am using lazy loading and did manage to get this working. I thought it would not but seems ok after a bit of fiddling. I pretty much followed the guide here: https://theinfogrid.com/tech/developers/angular/enabling-hot-module-replacement-angular-6/

Its quite slow though (not sure if it will be faster than normal live-reload or not yet...), and although it does do the replace without a full reload, it does not persist my model.

@wags1999
Copy link

@adzza24, was there any additional steps you had to complete? I followed the same guide, and the whole app still reloads when I make changes to a lazy-loaded module.

@adzza24
Copy link

adzza24 commented Oct 15, 2018

@wags1999 Check if your entire app is reloading - for me, just the lazy route I was on seemed to reload but the app itself was still running. Any data in the route was lost, but app-wide settings were saved. This also caused issues for me with Modals being detected as open globally, when they had been closed in the local route.

I stopped using it in the end because it proved slower than waiting for the app to reload! All in all, I think there is a solid reason that the guys at Angular did not include hot reloading by default! Maybe in a future release they will build in between support for it, but right now its a hack and for large apps its not really a viable solution.

That said, I will post my code here:

~/src/hmr.ts

import { NgModuleRef, ApplicationRef } from '@angular/core';

import { createNewHosts } from '@angularclass/hmr';

export const hmrBootstrap = (module: any, bootstrap: () => Promise<NgModuleRef<any>>) => {
  let ngModule: NgModuleRef<any>;
  module.hot.accept();
  bootstrap().then(mod => (ngModule = mod));
  module.hot.dispose(() => {
    const appRef: ApplicationRef = ngModule.injector.get(ApplicationRef);
    const elements = appRef.components.map(c => c.location.nativeElement);
    const makeVisible = createNewHosts(elements);
    ngModule.destroy();
    makeVisible();
  });
};

~/src/main.ts

import { enableProdMode } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';

import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

import { hmrBootstrap } from './hmr';

import 'hammerjs';

const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule);

if (environment.production) {
  enableProdMode();
}
if (environment.hmr) {
  if (module['hot']) {
    console.log('HMR enabled');
    hmrBootstrap(module, bootstrap);
  } else {
    console.error('HMR was requested but is not enabled for webpack-dev-server!');
    console.log('Are you using the --hmr flag for ng serve?');
  }
}
else {
  bootstrap()
    .catch(err => console.log(err));
}

~/src/tsconfig.app.json

{
  "extends": "../tsconfig.json",
  "compilerOptions": {
    "outDir": "../out-tsc/app",
    "module": "es2015",
    "types": ["node"]
  },
  "exclude": [
    "src/test.ts",
    "**/*.spec.ts"
  ]
}

~/src/environments/environment.hmr.ts

export const environment: Environment = {
  production: false,
  hmr: true,
};

~/angular.json

{
  ...
  "projects": {
    "myApp": {
      ...
      "architect": {
        "build": { ... },
          "configurations": {
            "production": { ... },
            "hmr": {
              "fileReplacements": [
                {
                  "replace": "src/environments/environment.ts",
                  "with": "src/environments/environment.hmr.ts"
                }
              ]
            }
          }
        },
      }
    },
  },
}

npm script

ng serve --configuration hmr --live-reload true

@topaxi
Copy link
Author

topaxi commented Oct 19, 2018

I think each lazy loaded module should be a "hot reloading" module aswell. The readme only shows how to do hot reloading on the root module with bootstrap though.

@nguyenbathanh
Copy link

I am facing the same issue, this makes it useless.

@JurajMlich
Copy link

Not sure what I did different, but it seems to work without a problem for me. I have written a guide how to set it properly, so you might want to check it out - https://medium.com/@jurajmlich/angular-7-hot-reload-ngrx-4b0368c5bf22

@wags1999
Copy link

wags1999 commented Jan 9, 2019

@JurajMlich, the link you provided shows a "Suspended" message. Can you provide a new link?

@JurajMlich
Copy link

@wags1999 that's weird... anyway, I made a copy for you:
https://gist.github.com/JurajMlich/f51dd079c5e71c4a8db532b9dfde0ab8

Hope it helps! :)

@wags1999
Copy link

wags1999 commented Feb 4, 2019

I've come up with a solution for using Angular HMR with lazy-loaded components, where each component's module gets lazy-reloaded - without reloading the entire route. It's saved our development team tons of time already, as our Angular solution is fairly large. The time from making a change in source code to the app being usable again on a developer workstation has gone from 18 seconds to 6 seconds.

I hope others can benefit from this solution, so I've setup an example app here:
https://github.com/wags1999/angular-hmr-lazy-components

@twadzinski
Copy link

I've come up with a solution for using Angular HMR with lazy-loaded components, where each component's module gets lazy-reloaded - without reloading the entire route. It's saved our development team tons of time already, as our Angular solution is fairly large. The time from making a change in source code to the app being usable again on a developer workstation has gone from 18 seconds to 6 seconds.

I hope others can benefit from this solution, so I've setup an example app here:
https://github.com/wags1999/angular-hmr-lazy-components

Thanks for this! The example code shows how to make it work with a component constructed manually (this.dynamicComponentSvc.createComponent()). Any idea how to make it work if Router is used (which constructs the components internally)? I'm trying to make it work by manually adding the new routes component to the openComponents object, but now sure what to do for outlet. I want to somehow maybe point that to the router outlet...

@haskelcurry
Copy link

haskelcurry commented Mar 24, 2019

@wags1999 Thanks for your solution, looks promising!! But I have the same question as @twadzinski . How to make it work with regular router? How exactly did you integrate this solution in your existing large project?

@wags1999
Copy link

@twadzinski, @mtuzinskiy, I just updated https://github.com/wags1999/angular-hmr-lazy-components with an example of reloading lazy-loaded routes. It's actually much simpler than reloading dynamically/manually created components - no need to use the dynamicComponentService at all. Check out the new method added in the hmr-module-helper - it essentially just resets the router (so the cached moduleFactoryLoader is released) and then re-navigates to the route. I hope it helps!

@haskelcurry
Copy link

@wags1999 First of all, thank you!
I hoped that your solution would possibly help me with the issue that I have -- HMR destroys the state i.e. clears the inputs etc each time -- but unfortunately it didn't help 🙁 The issue is: I have a simple login component. I fill in the inputs with some values. Then I slightly change something in the code (e.g. SCSS or HTML of the component). HMR reloads the component, but the inputs get cleared! To me it ruins the whole idea of HMR, making it equivalent to auto-reload.
I found out that without lazy loading the module, it's not the case: the input values persist.
I hoped that maybe your approach would fix that for lazy loaded modules, too... But it didn't.
I guess I just need to bear with the fact that HMR just works bad with Angular (in most cases).
Anyway, let me thank you again for your great supportive spirit!

@Prinsn
Copy link

Prinsn commented Aug 14, 2019

@wags1999 and @JurajMlich's projects naively both work in tandem (have it working but not largely tested), they appear to solve different problems.

Jura's sort of works with the lazy loading, but it implements basic HMR cleanly, and Wags' serves as an improved method for the lazy loading.

I'm curious about an answer to @mtuzinskiy's case, as that'd be optimal, but the core thing I was looking for was speeding up development.

@haskelcurry
Copy link

haskelcurry commented Aug 16, 2019

I just want to make it clear: Angular HMR doesn't work with lazy loaded modules.

@sikemausa
Copy link

@wags1999 Thanks very much for the solution, about exactly what I was looking for and should cut down on dev time considerably. Has anyone had any luck implementing the HMR in any capacity with Angular Material dialogs? Seems like it's a stretch as even the traditional hmr without the lazy loads seems to run into issues, but would love to hear other's experiences.

@webia1
Copy link

webia1 commented Oct 18, 2019

HMR ( with lazy loaded modules) is and remains as a problem unfortunately :/

  1. https://stackoverflow.com/questions/48236160/angular-cli-hmr-with-lazy-loaded-routes-hot-reloads-the-whole-thing
  2. https://stackoverflow.com/questions/55355133/angular-7-hmr-hot-module-replacement-does-not-work-if-any-route-resolve-invo

@kodeine
Copy link

kodeine commented Dec 23, 2021

wow, this has been unsolved for a long while now.

@ristomatti
Copy link

ristomatti commented Dec 23, 2021

@kodeine TLDR; You might actually be able to work around this but the result might not be what you were expecting to gain.

I received a notification as I had subscribed to this issue while working for my previous customer. This was one of the things I spent a lot of time trying to work around, trying all kinds of workarounds I found. It was a project team of ~20 developers working with a big codebase, each waiting 5-10 seconds for the view to reload. The app had views that required going through several steps to get back to what you were working on, so HMR not working wasted huge amount of developer time (= money)!

When Angular 11 came out, together with a team mate we were able to get HMR kind of working, even on lazy loaded ruotes... but not on all routes. I never figured what the key difference was. I remember using JIT compilation resulted in 2x faster reloads while under specific routes, only HMR on Ivy actually worked.

The funny thing is that after reaching this "impossible" goal, it was only for to realize HMR was almost useless unless you were using a global state management (like ngrx). Decision of not using ngrx had been made two years before I joined the team, so it was not a realistic thing to change. Out of curiosity, I set up a starter ngrx project. After testing it out for a while, to my great dismay, I finally understood why HMR likely has not been a big priority for the Angular team. Even when it works, it is not the same as with for example React. Even just a small change in CSS in a leaf component causes the app to reload, if not requiring an actual hard reload: angular/angular-cli#19867

Now on that issue thread I see a very familiar sight:

image

This was what I ended up seeing almost every time I tried to find "the proper way to do X". Curiously, I remember being able to get many of the supposedly impossible things working in the end. Many times this required digging into Angular source code and brute forcing everything I could imagine. This did not give me a very good picture of the state of the community. My theory is that Angular is mainly popular within large corporations, which each might go through solving these issues without sharing how it was done. To me it seemed like there was only a single guy who was trying to innovate and be active on the community.

During this ~9 month contract, I was mainly focusing on fixing the issues others had given up, refactoring this to be more "by the book" and doing various incremental improvements on the build process. I then jumped the ship when I got an opportunity to work with a fresh codebase, in React/Next.js. Now with Next.js's split second hot reloads, going through this frustration was a big reminder why I'll be avoiding Angular the best I can.

I was not thrilled to work with it when I joined that project but I decided to give Angular a fare second chance to prove me wrong as initially AngularJS was a big part of myself getting interested in JS. After few months, I ended being go-to-guy in Angular and TypeScript related issues within the project. At that point I had spent tens of hours of my free time reading various articles, going through random code snippets found by googling. What a was of time it was.

@webia1
Copy link

webia1 commented Dec 24, 2021

angular/angular-cli#19867 (closed but not solved)

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