Clément Flodrops Clément Flodrops - 3 months ago 25
TypeScript Question

NG2: angular2-webpack-starter - what is the purpose of HMR?

I'm cleaning up my angular2 project and for many reasons, I've decided to start with a seed. This one.

This seed uses HMR but I don't fully understand what is the purpose of that.

At the beginning, I was thinking that HMR was about dynamical loading and
replacing component while the web app is running.

But since I've put my eyes on the

app.service.ts
, I've got lost. Here is the code of this service :

import { Injectable } from '@angular/core';
import { HmrState } from 'angular2-hmr';

@Injectable()
export class AppState {
// @HmrState() is used by HMR to track the state of any object during a hot module replacement
@HmrState() _state = { };

constructor() {

}

// already return a clone of the current state
get state() {
return this._state = this._clone(this._state);
}
// never allow mutation
set state(value) {
throw new Error('do not mutate the `.state` directly');
}


get(prop?: any) {
// use our state getter for the clone
const state = this.state;
return state[prop] || state;
}

set(prop: string, value: any) {
// internally mutate our state
return this._state[prop] = value;
}


_clone(object) {
// simple object clone
return JSON.parse(JSON.stringify( object ));
}
}


I was thinking that service simply provides a space to store some data. After all, this is just an example.

But this line did confuse me:
@HmrState() _state = { };
. Is this service using HMR to manage data that we can manage with
this.appState.set('value', value);
(this is from the HomeComponent) like a little Redux's store (without actions, dispatcher, blabla) ?

What is the purpose of the decorator
@HmrState()
here ?

Thanks.

Answer

When I had a first look at angular2-hmr I was surprised as well. I thought it is something like a hot swap, but it is not really one. At least from what I see when I use it.

It looks like it always reloads the application regardless of the changes type. However it can restore the state of the swapped objects. The intention of @HmrState() is to restore the component's state when the application is reloaded.

Let's take a look at a small example. We have a form with an input which is bound (with ngModel or formControl) to some component's property:

@Component({
  template: `
    <input [(ngModel)]="inputValue" />
    <button (click)="click()">Click me</button>
  `
})
export class MyComponent {

  public inputValue: string;

  public click() {
    console.log(this.inputValue);
  }

}

We type in some value, e.g. 'test123' and click the button. It works.

Then suddenly we realise: the log description is missing. So we go to our code and add it:

@Component({
  template: `
    <input [(ngModel)]="inputValue" />
    <button (click)="click()">Click me</button>
  `
})
export class MyComponent {

  inputValue: string;

  public click() {
    console.log('inputValue:', this.inputValue);
  }

}

Then the component's code is changed, HMR replaces it and we realise that the inputValue is lost.

To restore the value during the HMR process angular2-hmr needs some information about the object state before it was wiped out. Here the @HmrState() comes into play: it points out to the state that should be restored. In other words to make the first code snippet work with HMR the following should be done:

@Component({
  template: `
    <input [(ngModel)]="state.inputValue" />
    <button (click)="click()">Click me</button>
  `
})
export class MyComponent {

  @HmrState() public state = {
    inputValue: ''
  }

  public click() {
    console.log(this.state.inputValue);
  }

}

The state is now known to the HMR processor and it can use the state to restore our value. Now when we change the component code to:

@Component({
  template: `
    <input [(ngModel)]="state.inputValue" />
    <button (click)="click()">Click me</button>
  `
})
export class MyComponent {

  @HmrState() public state = {
    inputValue: ''
  }

  public click() {
    console.log('inputValue:', this.state.inputValue);
  }

}

it magically reloads our application and the inputValue's value is preserved.

Comments