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

HMR configuration - Live Reload still enabled #17324

Closed
StefanKoenigMUC opened this issue Mar 27, 2020 · 28 comments · Fixed by #18788
Closed

HMR configuration - Live Reload still enabled #17324

StefanKoenigMUC opened this issue Mar 27, 2020 · 28 comments · Fixed by #18788
Assignees
Milestone

Comments

@StefanKoenigMUC
Copy link

Hi all,

i've just tried to setup HMR for my angular 9 application- which is working fine for changes within my SCSS files (... stylesheets are updated without page reload).

But it doesn't work out for markup/component files - changes there still trigger a reload of the whole page.

I assume, that I've done sth. wrong, but I could not figure out what (due to this message during startup):

NOTICE: Hot Module Replacement (HMR) is enabled for the dev server.
The project will still live reload when HMR is enabled,
but to take advantage of HMR additional application code is required'
(not included in an Angular CLI project by default).'
See https://webpack.js.org/guides/hot-module-replacement
for information on working with HMR for Webpack.
To disable this warning use "hmrWarning: false" under "serve" options in "angular.json".

My configuration is done accordingly to the 'official' but deprecated docs: https://github.com/angular/angular-cli/wiki/stories-configure-hmr

angular.json:

[...]
          "configurations": {
            "hmr": {
              "fileReplacements": [{
                "replace": "src/environments/environment.ts",
                "with": "src/environments/environment.hmr.ts"
              }]
            },
            "production": {
              "fileReplacements": [{
                "replace": "src/environments/environment.ts",
                "with": "src/environments/environment.prod.ts"
              }],
[...]

        "serve": {
          "builder": "@angular-devkit/build-angular:dev-server",
          "options": {
            "browserTarget": "PortfolioManagerWebClient:build"
          },
          "configurations": {
            "production": {
              "browserTarget": "PortfolioManagerWebClient:build:production"
            },
            "hmr": {
              "hmr": true,
              "browserTarget": "PortfolioManagerWebClient:build:hmr"
            }
          }
        },

package.json:

"hmr": "ng serve --configuration=hmr",

and env. file (src\environments\environment.hmr.ts)

export const environment = {
	production: false,
	hmr: true,
[...]

I couldn't figure out, that there is sth. obvious wrong. Could you advise me there?
Thanks in advance!

@alan-agius4 alan-agius4 added area: devkit/build-angular needs: discussion On the agenda for team meeting to determine next steps labels Mar 30, 2020
@ngbot ngbot bot modified the milestone: needsTriage Mar 30, 2020
@damienix
Copy link

damienix commented Mar 30, 2020

Hey, I recently tried to run HMR on Angular and it worked fine... before upgrading Angular from 9.0.7 to 9.1.0. I suspect this is related to webpck upgrades even tough these are minor/patch changes:
webpack 4.41.2-> 4.41.6
webpak-dev-server 3.9.0->3.10.3

My symptoms are that the HMR in WDS claims to be working, browser even properly detects HMR changes. The problem is that the changes don't seem to be downloaded.

Before:
9_0_7

After:
9_10_0

I didn't manage to nail down the issue :( Any idea why this could break?

BTW, I know that the last official claim is that HMR in Angular is not supported, but @angular Team, are there any considerations about supporting HMR? Don't you use it internally? :) It's available OOB in Vue, React, Flutter etc.

@damienix
Copy link

I found that adding inline=true to webpack-dev-server (recommended for HMR) fixes the problem for me. In my case I've added this in a custom builder:

const {createBuilder} = require('@angular-devkit/architect');
const {executeDevServerBuilder} = require('@angular-devkit/build-angular');

const buildWithPlugins = (options, context, transforms) => {
  const transformsData = transforms || {};
  transformsData.webpackConfiguration = (webpackConfig) => {
    webpackConfig.devServer.inline = true;
    return webpackConfig;
  };

  return executeDevServerBuilder(options, context, transformsData);
};

exports.default = createBuilder(buildWithPlugins);

P.S. This may suggest that HMR entry point may have been removed in a new version of angular, but I'm not sure.

@dgp1130
Copy link
Collaborator

dgp1130 commented Apr 2, 2020

After some discussion with the rest of the CLI team, the current way to use HMR with Angular isn't really supported and requires a very specific knowledge of the magic incantations to make work. We also haven't really tested this flow in some time, so how it plays with v9 and Ivy is somewhat unknown.

We definitely want to support HMR better and that will take a good chunk of work. The best way to move forward is probably:

  • Understand the current state and if/how HMR works with latest Angular.
  • Update documentation/best practices to explain how to make it work (or clarify that it just won't).
  • Scope the work needed to get HMR to a good state and plan accordingly.

@alan-agius4, can you (or someone else) take a stab at the first two?

@dgp1130 dgp1130 removed the needs: discussion On the agenda for team meeting to determine next steps label Apr 2, 2020
@dgp1130 dgp1130 modified the milestones: needsTriage, Backlog Apr 2, 2020
@ngbot ngbot bot modified the milestones: Backlog, needsTriage Apr 2, 2020
@damienix
Copy link

damienix commented Apr 2, 2020

I really appreciate your answer @dgp1130.

It's worth adding that deprecated HMR solution only reinstantiates the whole app module which is not true HMR. True HMR works on component level and in the best case is able to hot-swap style or template-only changes of a component while preserving its state.

I'd be really interested in following the topics you've listed.

@jadengis
Copy link

jadengis commented Apr 3, 2020

I also ran into the issue described by @damienix, and bisecting through my git history pointed to my upgrade from 9.0.7 to 9.1.0 as the cause of the hot reloading no longer working.

I have been using the ngxs/hmr-plugin to get hmr working with angular, and have found it to be a great boon to my productivity as the browser reloads much faster than a full refresh, even if the state is not retained.

I would really like to see an OOB hmr solution for angular in the cli.

@indraraj26
Copy link

indraraj26 commented Apr 18, 2020

Hi @jadengis , after adding that ngxs/hmr-plugin do we need to alter something at angular side ?
ng serve -c=hmr will work?

Yes we really need HMR in angular every framework has that feature hot reload without notice (silently)

@jadengis
Copy link

@indraraj26 The @ngxs/hmg-plugin no longer works with Angular 9.1. I'm not sure what happened on the Angular side, but it looks like the angular app is somehow ignoring the the hot reload. The webpack server still generates the hot update modules when you make a code change, but the angular app doesn't listen to that event and trigger the hot module reload. I'm not sure why because I'm not familiar with the CLI code.

@mhamri
Copy link

mhamri commented Apr 27, 2020

After some discussion with the rest of the CLI team, the current way to use HMR with Angular isn't really supported and requires a very specific knowledge of the magic incantations to make work. We also haven't really tested this flow in some time, so how it plays with v9 and Ivy is somewhat unknown.

We definitely want to support HMR better and that will take a good chunk of work. The best way to move forward is probably:

* Understand the current state and if/how HMR works with latest Angular.

* Update documentation/best practices to explain how to make it work (or clarify that it just won't).

* Scope the work needed to get HMR to a good state and plan accordingly.

@alan-agius4, can you (or someone else) take a stab at the first two?

if it's in such an unstable state, why it's available in the ng serve? what a user supposed to expect from ng serve --hmr?

it's part of doc in v9.1.3

@dgp1130
Copy link
Collaborator

dgp1130 commented Apr 27, 2020

@mhamri, that's part of the action items here. We need to figure out ourselves what a user should expect in v9 and how we can get it to a more consistent state. That might mean just adjusting documentation, it might mean it won't work and we should just remove the flag until it does, or it might mean there are some simple fixes we can make to get it back to a usable state.

We still need to do the relevant investigation to figure out the best way of moving forward.

@mhamri
Copy link

mhamri commented Jun 15, 2020

@dgp1130 I'm totally fine with it. investigating and figuring out is part of software development. my concern is, if it's a research item on v9 and the angular team is aware of it then why it doesn't reflect in the documentation? not everyone refers to here to figure out what is working and what is not working. an update to the documentation would be nice and if you like I can make the PR

@ahnpnl
Copy link

ahnpnl commented Jun 27, 2020

I also vote for support HMR OOB for Angular CLI. I use HMR a lot during development because it speeds up significantly comparing to full page reload, especially when doing styling.

@murbanowicz
Copy link

@dgp1130 Is this something (HMR) which is in work now or we cannot expect HMR working with Angular 9+ any time soon?

@hhammoud
Copy link

hhammoud commented Jul 4, 2020

@damienix can you pls provide an example on how you solved it? with custom builder. I cant seem to make it work. The whole page still reloads. I even downgraded angular to 9.0.7 still any change css,html or code, reloads the whole app.

@damienix
Copy link

damienix commented Jul 8, 2020

@hhammoud do you have Ivy enabled? The solution with the builder still was not working properly for me with Ivy.

@Y2zz
Copy link

Y2zz commented Aug 28, 2020

Why can such a problem exist for so long and cannot be solved?

I haven't got HMR to work normally since angular 4

@Y2zz
Copy link

Y2zz commented Aug 28, 2020

The steps to reproduce are simple

  1. You only need to create a new project, ng new test
  2. Change the script in package.json: "start": "ng serve" to "start":"ng serve --hmr"
  3. Run npm start
  4. Change the content in app.component.html and observe whether hmr takes effect in the developer tools

@alan-agius4
Copy link
Collaborator

@Y2zz, as per the warning when running ng serve --hmr, you need to configure your application to take full advantage HMR.

The project will still live reload when HMR is enabled, but to take full advantage of HMR
additional application code which is not included by default in an Angular CLI project is required.
See https://webpack.js.org/guides/hot-module-replacement for information on working with HMR for Webpack.

This is because to make HMR work for JS you will need to do some changes to your application based on which HMR modules you use example: https://www.npmjs.com/package/@angularclass/hmr, https://www.npmjs.com/package/@ngxs/hmr-plugin etc...

I've setup a new application and configured it to use HMR using @angularclass/hmr, I am not using any state framework such as NGRX or NGXS (For these you'll need to add further configuration or use a different dedicated HMR module).

Here's the steps I followed:

ng new hmr-test --routing
cd hmr-test
npm i @angularclass/hmr --dev

Replace main.ts with

import { enableProdMode, NgModuleRef, ApplicationRef } from '@angular/core';
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { createNewHosts } from '@angularclass/hmr';

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

if (environment.production) {
  enableProdMode();
}

declare var module: any;
async function hmrBootstrap(bootstrap: () => Promise<NgModuleRef<AppModule>>) {
  module.hot.accept();
  const ngModule = await bootstrap();
  module.hot.dispose(() => {
    const appRef = ngModule.injector.get(ApplicationRef);
    const elements = appRef.components.map(c => c.location.nativeElement);
    const makeVisible = createNewHosts(elements);
    ngModule.destroy();
    makeVisible();
  });
}

const bootstrapCall = () => platformBrowserDynamic().bootstrapModule(AppModule);
if (module['hot']) {
  hmrBootstrap(bootstrapCall);
} else {
  bootstrapCall();
}
ng serve --hmr

References:
https://medium.com/echohub/implementation-of-hmr-hot-module-replacement-to-angular-project-f7bca523bd16
https://medium.com/@marianbenat/angular-6-hmr-ngrx-store-240024cad2f6
https://www.npmjs.com/package/@angularclass/hmr
https://www.npmjs.com/package/@ngxs/hmr-plugin
https://webpack.js.org/guides/hot-module-replacement

Based on the above, HMR seems to be working as expected.

@haskelcurry
Copy link

haskelcurry commented Sep 3, 2020

@alan-agius4 No it doesn't. It worked prior to Angular 9.1, but now the reload stucks all the time / has other quirks described above. Try it for yourself with more or less complex app (not just AppModule).
The real problem is that the Angular officially doesn't support HMR, and it'll keep breaking all the time until it's supported officially.
Modern UI development cannot be imagined without HMR nowdays, all frameworks support it (it's a Webpack feature right?), but not Angular.
I don't care about Ivy or any other "cool feature" of the framework, if it doesn't support HMR it's a garbage for me, as well as for most of the other experienced frontend developers, I believe.

@alan-agius4
Copy link
Collaborator

alan-agius4 commented Sep 3, 2020

@mtuzinskiy, while I do agree that we should document and support HMR better. We need a more information in which use-case HMR is not working for you. Your application structure and architecture might also effect the outcome of HMR.

Without a reproduction that demonstrates the broken behaviour it's hard to tell what issue you are experiencing because of the number of variables that play a role in HMR, such as AOT/JIT compilation, Rendering engine (VE or Ivy), type of change, and application structure and architecture.

I did however continue to look at this and in Ivy JIT mode, I get an error sometimes (angular/angular#35265);

polyfills.js:3290 Unhandled Promise rejection: Type AppComponent is part of the declarations of 2 modules: AppModule and AppModule! Please consider moving AppComponent to a higher module that imports AppModule and AppModule. You can also create a new NgModule that exports and includes AppComponent then import that NgModule in AppModule and AppModule. ; Zone: <root> ; Task: Promise.then ; Value: Error: Type AppComponent is part of the declarations of 2 modules: AppModule and AppModule! Please consider moving AppComponent to a higher module that imports AppModule and AppModule. You can also create a new NgModule that exports and includes AppComponent then import that NgModule in AppModule and AppModule.
    at verifySemanticsOfNgModuleDef (vendor.js:68332)
    at Function.get (vendor.js:68267)
    at getInjectorDef (vendor.js:42825)
    at R3Injector.processInjectorType (vendor.js:53567)
    at vendor.js:53439
    at vendor.js:43564
    at Array.forEach (<anonymous>)
    at deepForEach (vendor.js:43564)
    at new R3Injector (vendor.js:53439)
    at createInjectorWithoutInjectorInstances (vendor.js:53413) Error: Type AppComponent is part of the declarations of 2 modules: AppModule and AppModule! Please consider moving AppComponent to a higher module that imports AppModule and AppModule. You can also create a new NgModule that exports and includes AppComponent then import that NgModule in AppModule and AppModule.
    at verifySemanticsOfNgModuleDef (http://localhost:4200/vendor.js:68332:15)
    at Function.get (http://localhost:4200/vendor.js:68267:21)
    at getInjectorDef (http://localhost:4200/vendor.js:42825:13)
    at R3Injector.processInjectorType (http://localhost:4200/vendor.js:53567:19)
    at http://localhost:4200/vendor.js:53439:48
    at http://localhost:4200/vendor.js:43564:76
    at Array.forEach (<anonymous>)
    at deepForEach (http://localhost:4200/vendor.js:43564:11)
    at new R3Injector (http://localhost:4200/vendor.js:53439:9)
    at createInjectorWithoutInjectorInstances (http://localhost:4200/vendor.js:53413:12)

This seems to be caused by the ownerNgModule cache Map in @angular/core https://github.com/angular/angular/blob/d1ea1f4c7f3358b730b0d94e65b00bc28cae279c/packages/core/src/render3/jit/module.ts#L254-L273 because resetCompiledComponents https://github.com/angular/angular/blob/d1ea1f4c7f3358b730b0d94e65b00bc28cae279c/packages/core/src/render3/jit/module.ts#L361 is never invoked when the NgModule is destroyed. Invoking ɵresetCompiledComponents(); right before ngModule.destroy(); gets ride or this error.

  module.hot.dispose(() => {
    const appRef = ngModule.injector.get(ApplicationRef);
    const elements = appRef.components.map(c => c.location.nativeElement);
    const makeVisible = createNewHosts(elements);
    ɵresetCompiledComponents();
    ngModule.destroy();
    makeVisible();
  });

Let me chime in @JoostK who did some HMR fixes in the past in the FW.

@haskelcurry
Copy link

haskelcurry commented Sep 3, 2020

@alan-agius4 This is exactly what happens for me now:

@indraraj26 The @ngxs/hmg-plugin no longer works with Angular 9.1. I'm not sure what happened on the Angular side, but it looks like the angular app is somehow ignoring the the hot reload. The webpack server still generates the hot update modules when you make a code change, but the angular app doesn't listen to that event and trigger the hot module reload. I'm not sure why because I'm not familiar with the CLI code.

Except that I don't use @ngxs/hmg-plugin. I made HMR work in Angular 9 using the approaches that you have mentioned, but after update to Angular 9.1 the app started to ignore the hot reload.
I've spent enough time on making this work in 9.0, and I don't feel like I want to continue digging it without official docs on HMR in Angular. Then I could do it following the official guide, and not some 3rd party tutorials, and it would be the best possible foundation for further work on this, IMHO.
Currently, the official docs state that

Documentation below is deprecated and we no longer accept PRs to improve this.

https://github.com/angular/angular-cli/wiki/stories-configure-hmr

@alan-agius4
Copy link
Collaborator

The documentation in https://github.com/angular/angular-cli/wiki is for Angular CLI version 1 - 6 and hence the reason why that documentation is deprecated and no longer maintained. We'll be removing this documentation in the coming weeks because it's no longer relevant in many cases.

At the time we created the new documentation, we felt that it's best not to provide documentation for 3rd party solutions.

That being said, maybe we can offer a simple documentation on how to use HMR for most usecases or a schematic for some basic use cases when angular/angular#37474 API is public. Where eventually users will be able to enhance if they use state management libraries.

I also think that in many cases the below should suffice: (Un-tested)

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

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

if (environment.production) {
  enableProdMode();
}

declare var module: any;
if (module['hot']) {
  module['hot'].accept();
  module['hot'].dispose(() => ɵresetCompiledComponents());
}

platformBrowserDynamic().bootstrapModule(AppModule);

@haskelcurry
Copy link

@alan-agius4 Schematic would be awesome! 👏

@haskelcurry
Copy link

haskelcurry commented Sep 3, 2020

@alan-agius4 That ɵresetCompiledComponents really does the trick for me, I've just tried it out.
It's not mentioned in "classical" HMR tutorials for Angular, because they are all from pre-Ivy times... actually I see this method for the very first time.
Thank you for the suggestion! Please elaborate 🙂

@alan-agius4
Copy link
Collaborator

The ɵresetCompiledComponents will reset the mapping components and NgModules.

Failing to invoke this, will result in an error because the component will be referenced in multiple NgModules, the module instance before the change and the instance after the change.

@alan-agius4 alan-agius4 added feature Issue that requests a new feature and removed needs: investigation Requires some digging to determine if action is needed labels Sep 16, 2020
@ngbot ngbot bot modified the milestones: needsTriage, Backlog Sep 16, 2020
alan-agius4 added a commit that referenced this issue Sep 25, 2020
…ement (HMR)

With this change we configure HMR internally and therefore users which want to use basic HMR functionality will not longer be required to change their application code.

This is important because previously a lot of users missed out on HMR or reported a broken behaviour. This also gives novice users a better chance to appreciate HMR and Angular because of the zero effort required to use HMR.

Closes #17324
@jadengis
Copy link

@alan-agius4 I see you closed this ticket with a PR enabling HMR out of the box. Thanks so much for this! This is exactly what I've been looking for. I would just like to ask when can we expect to see this functionality in the CLI?

@alan-agius4
Copy link
Collaborator

This will be available in v11.0.0-next.4, which will should be released on NPM next Wednesday.

When it’s released you can test it by updating your application to use the pre-release version using the below command

ng update @angular/cli -—next

@murbanowicz
Copy link

Does it mean it will be true HMR which will e.g. keep the state like in React or Vue or we will still be loosing state even without full page reload?

@angular-automatic-lock-bot
Copy link

This issue has been automatically locked due to inactivity.
Please file a new issue if you are encountering a similar or related problem.

Read more about our automatic conversation locking policy.

This action has been performed automatically by a bot.

@angular-automatic-lock-bot angular-automatic-lock-bot bot locked and limited conversation to collaborators Oct 27, 2020
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

Successfully merging a pull request may close this issue.