angular-hmr icon indicating copy to clipboard operation
angular-hmr copied to clipboard

Lazy Routes reload whole App

Open topaxi opened this issue 6 years ago • 22 comments

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

topaxi avatar May 22 '18 12:05 topaxi

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

maxencefrenette avatar Jun 27 '18 14:06 maxencefrenette

same issue

ronsc avatar Aug 22 '18 09:08 ronsc

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.

adzza24 avatar Oct 08 '18 13:10 adzza24

@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.

wags1999 avatar Oct 10 '18 20:10 wags1999

@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

adzza24 avatar Oct 15 '18 11:10 adzza24

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.

topaxi avatar Oct 19 '18 17:10 topaxi

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

nguyenbathanh avatar Dec 06 '18 15:12 nguyenbathanh

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

JurajMlich avatar Jan 02 '19 21:01 JurajMlich

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

wags1999 avatar Jan 09 '19 15:01 wags1999

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

Hope it helps! :)

JurajMlich avatar Jan 09 '19 19:01 JurajMlich

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

wags1999 avatar Feb 04 '19 14:02 wags1999

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...

twadzinski avatar Feb 28 '19 20:02 twadzinski

@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?

haskelcurry avatar Mar 24 '19 21:03 haskelcurry

@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!

wags1999 avatar Mar 30 '19 18:03 wags1999

@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 :slightly_frowning_face: 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!

haskelcurry avatar Mar 31 '19 08:03 haskelcurry

@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.

Prinsn avatar Aug 14 '19 19:08 Prinsn

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

haskelcurry avatar Aug 16 '19 22:08 haskelcurry

@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.

sikemausa avatar Aug 28 '19 22:08 sikemausa

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

webia1 avatar Oct 18 '19 16:10 webia1

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

kodeine avatar Dec 23 '21 14:12 kodeine

@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: https://github.com/angular/angular-cli/issues/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.

ristomatti avatar Dec 23 '21 22:12 ristomatti

https://github.com/angular/angular-cli/issues/19867 (closed but not solved)

webia1 avatar Dec 24 '21 08:12 webia1