Skip to content

Commit

Permalink
feat(integration): add HMR for integration example with docs (ngxs#660)
Browse files Browse the repository at this point in the history
* feat(integration): add HMR for integration example with docs

* chore: update yarn.lock
  • Loading branch information
splincode authored and markwhitfeld committed Nov 14, 2018
1 parent 57e8ee4 commit 6149cec
Show file tree
Hide file tree
Showing 12 changed files with 501 additions and 246 deletions.
12 changes: 12 additions & 0 deletions angular.json
Original file line number Diff line number Diff line change
Expand Up @@ -100,6 +100,14 @@
"with": "/integration/environments/environment.prod.ts"
}
]
},
"hmr": {
"fileReplacements": [
{
"replace": "/integration/environments/environment.ts",
"with": "/integration/environments/environment.hmr.ts"
}
]
}
}
},
Expand All @@ -111,6 +119,10 @@
"configurations": {
"production": {
"browserTarget": "integration:build:production"
},
"hmr": {
"hmr": true,
"browserTarget": "integration:build:hmr"
}
}
},
Expand Down
1 change: 1 addition & 0 deletions docs/SUMMARY.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@
* [Life-cycle](advanced/life-cycle.md)
* [Meta Reducers](advanced/meta-reducer.md)
* [Shared State](advanced/shared-state.md)
* [Hot Module Replacement](advanced/hmr.md)
* Recipes
* [Authentication](recipes/authentication.md)
* [Caching](recipes/cache.md)
Expand Down
162 changes: 162 additions & 0 deletions docs/advanced/hmr.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
# Hot Module Replacement

Hot Module Replacement (HMR) is a WebPack feature to update code in a running app without rebuilding it. This results in faster updates and less full page-reloads.
In order to get HMR working with Angular CLI we first need to add a new environment and enable it.

### Add environment for HMR

In this step will configure the Angular CLI environments and define in which environment we enable HMR.
We will start out by adding and changing files in the `src/environments/` directory.
First we create a file called `src/environments/environment.hmr.ts` with the following contents:

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

Update `src/environments/environment.prod.ts` and add the hmr: false flag to the environment:

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

Lastly we edit `src/environments/environment.ts` and change the environment to:

```ts
export const environment = {
production: false,
hmr: false
};
```

Update angular.json to include an hmr environment as explained here and add configurations within build and serve to enable hmr.
Note that <project-name> here represents the name of the project you are adding this configuration to in angular.json.

```text
"build": {
"configurations": {
...
"hmr": {
"fileReplacements": [
{
"replace": "src/environments/environment.ts",
"with": "src/environments/environment.hmr.ts"
}
]
}
}
},
...
"serve": {
"configurations": {
...
"hmr": {
"hmr": true,
"browserTarget": "<project-name>:build:hmr"
}
}
}
```

Add the necessary types to src/tsconfig.app.json

```text
{
...
"compilerOptions": {
...
"types": ["node"]
},
}
```

Run ng serve with the flag --configuration hmr to enable hmr and select the new environment:

```bash
ng serve --configuration hmr
```

Create a shortcut for this by updating package.json and adding an entry to the script object:

```bash
"scripts": {
...
"hmr": "ng serve --configuration hmr"
}
```

### Add dependency for @angularclass/hmr and configure app

In order to get HMR working we need to install the dependency and configure our app to use it.

Install the @angularclass/hmr module as a dev-dependency

Create a file called src/hmr.ts with the following content:

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

Update src/main.ts to use the file we just created:

```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';

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

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

if (environment.hmr) {
if (module[ 'hot' ]) {
hmrBootstrap(module, bootstrap);
} else {
console.error('HMR 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));
}
```

### Starting the development environment with HMR enabled

Now that everything is set up we can run the new configuration:

```bash
npm run hmr
```

When starting the server Webpack will tell you that it’s enabled:

```bash
NOTICE Hot Module Replacement (HMR) is enabled for the dev server.
```

Now if you make changes to one of your components they changes should be visible automatically without a complete browser refresh.
12 changes: 11 additions & 1 deletion integration/app/app.component.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { Observable } from 'rxjs';
import { FormBuilder, FormArray } from '@angular/forms';

import { AddTodo, RemoveTodo, TodoState, SetPrefix, TodosState, LoadData } from './todo.state';
import { environment } from '../environments/environment';

@Component({
selector: 'app-root',
Expand Down Expand Up @@ -59,7 +60,16 @@ export class AppComponent {
extras: this.createExtras()
});

constructor(private store: Store, private formBuilder: FormBuilder) {}
constructor(private store: Store, private formBuilder: FormBuilder) {
AppComponent.checkHotModuleReplacement();
}

private static checkHotModuleReplacement() {
if (environment.hmr) {
console.clear();
console.log('HMR enabled');
}
}

addTodo(todo: string) {
this.store.dispatch(new AddTodo(todo));
Expand Down
5 changes: 5 additions & 0 deletions integration/environments/environment.hmr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export const environment = {
production: false,
hmr: true
};

3 changes: 2 additions & 1 deletion integration/environments/environment.prod.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export const environment = {
production: true
production: true,
hmr: false
};
8 changes: 2 additions & 6 deletions integration/environments/environment.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,4 @@
// The file contents for the current environment will overwrite these during build.
// The build system defaults to the dev environment which uses `environment.ts`, but if you do
// `ng build --env=prod` then `environment.prod.ts` will be used instead.
// The list of which env maps to which file can be found in `.angular-cli.json`.

export const environment = {
production: false
production: false,
hmr: false
};
15 changes: 15 additions & 0 deletions integration/hmr.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
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();
});
};
17 changes: 14 additions & 3 deletions integration/main.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,21 @@ import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app/app.module';
import { environment } from './environments/environment';

import { hmrBootstrap } from './hmr';

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

platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.log(err));
const bootstrap = () => platformBrowserDynamic().bootstrapModule(AppModule);

if (environment.hmr) {
if (module[ 'hot' ]) {
hmrBootstrap(module, bootstrap);
} else {
console.error('HMR 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));
}
2 changes: 1 addition & 1 deletion integration/tsconfig.app.integration.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
},
"outDir": "./out-tsc/app",
"module": "es2015",
"types": []
"types": ["node"]
},
"include": ["**/*.ts", "./polyfills.ts"],
"exclude": ["test.integration.ts", "**/*.spec.ts"]
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@
"// - APPS": "Run Apps in dev and with packaged modules",
"start": "ng serve --aot --open",
"serve:integration": "ng serve --prod --project integration #requires yarn package",
"serve:hmr:integration": "npm start -- --project integration --configuration hmr",
"// - BUILDING": "Run Apps in dev and with packaged modules",
"build": "yarn build:packages && ts-node tools/set-metadata",
"build:packages": "ts-node tools/package",
Expand Down Expand Up @@ -87,6 +88,7 @@
"@angular/platform-browser": "7.0.0",
"@angular/platform-browser-dynamic": "7.0.0",
"@angular/router": "7.0.0",
"@angularclass/hmr": "^2.1.3",
"@commitlint/cli": "^6.1.3",
"@commitlint/config-conventional": "^6.1.3",
"@types/deep-freeze-strict": "^1.1.0",
Expand Down
Loading

0 comments on commit 6149cec

Please sign in to comment.