Angular x NGXS アプリに、ngxs-labs/emitter を導入する

# 背景

Angular の状態管理ライブラリ　NGXS　について、Action 定義を省略する手法として ngxs-labs/emitter というものがあるようなので、こちらを導入してみる。

https://github.com/ngxs-labs/emitter

# ngxs-labs/emitter のメリットについて

ドキュメントを読むと、

- state で直接、 actions を登録するデコレーターを使える
- プロジェクト内で actions を作る必要がなくなる (本当に必要になるまでは)

ということなので、 actions を丸っと削除できるようだ。

イメージは下記画像のようになる。

![redux-er.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/599665/b9615b65-9588-2d84-bda7-211c946efece.png)

Action がなくなるので、かなりスッキリしそうな予感。

# インストール

```bash:bash
npm install @ngxs-labs/emitter
# or if you use yarn
yarn add @ngxs-labs/emitter
```

# AppModule にインポート

```typescript:src/app/app.module.ts
...
import { NgxsModule }              from '@ngxs/store';
import { NgxsEmitPluginModule }    from '@ngxs-labs/emitter';

...

@NgModule({
  ...
  imports: [
    ...
    NgxsModule.forRoot(...),
    NgxsEmitPluginModule.forRoot()
  ],
  ...
})
export class AppModule { }
```

# レシーバー

レシーバーは基本的な構成要素。
`@Receiver()` は state 内の静的メソッドをデコレートできる関数で、そのメソッドをエミッターに渡す。

```typescript:src/app/auth/auth.state.ts
import { Injectable }                    from '@angular/core';
import { Receiver }                      from '@ngxs-labs/emitter';
import { Selector, State, StateContext } from '@ngxs/store';

export interface AuthStateModel {
  isAuthenticated: boolean;
}

@State<AuthStateModel>({
  name: 'auth',
  defaults: {
    isAuthenticated: false
  }
})
@Injectable()
export class AuthState {

  @Receiver()
  static setAuthenticated(ctx: StateContext<AuthStateModel>) {
    ctx.setState({
      isAuthenticated: true
    });
  }

  @Receiver()
  static setUnauthenticated(ctx: StateContext<AuthStateModel>) {
    ctx.setState({
      isAuthenticated: false
    });
  }

  ...
}
```

# エミッター

エミッターは、基本的にコンポーネントとレシーバーの間の橋渡しをするもの。
`@Emitter()` は、新しいゲッターを定義するプロパティを修飾する関数で、
、エミッタブルなインターフェースにアクセスできるようにする。

```typescript:src/app/auth/auth.state.ts
import { Injectable }                                  from '@angular/core';
import { Emittable, Emitter, EmitterAction, Receiver } from '@ngxs-labs/emitter';
import { Selector, State, StateContext }               from '@ngxs/store';

// import { AuthAction } from './auth.actions';

export interface AuthStateModel {
  isAuthenticated: boolean;
}

@State<AuthStateModel>({
  name: 'auth',
  defaults: {
    isAuthenticated: false
  }
})
@Injectable()
export class AuthState {

  @Emitter(AuthState.setAuthenticated)   static actSetAuthenticated: Emittable<void>;
  @Emitter(AuthState.setUnauthenticated) static actSetUnauthenticated: Emittable<void>;

  @Receiver()
  static setAuthenticated(
    ctx:    StateContext<AuthStateModel>,
    action: EmitterAction<void>
  ) {
    ctx.setState({
      isAuthenticated: true
    });
  }

  @Receiver()
  static setUnauthenticated(
    ctx:    StateContext<AuthStateModel>,
    action: EmitterAction<void>
  ) {
    ctx.setState({
      isAuthenticated: false
    });
  }

  @Selector()
  static getIsAuth(state: AuthStateModel) {
    return state.isAuthenticated;
  }
}
```

```typescript:src/app/auth/auth.service.ts
...
import { AuthState } from './auth.state';

@Injectable()
export class AuthService {

  ...

  initAuthListener() {
    this.afAuth.authState.subscribe(user => {
      if (user) {
        AuthState.actSetAuthenticated.emit();
        ...
      } else {
        ...
        AuthState.actSetUnauthenticated.emit();
        ...
      }
    });
  }

  ...

}
```

# 感想

- かなりシンプルに書ける
    - 例えば、dispatch と書いていたところを emit で書くと、分かりやすく、かつ短く書ける
- 無駄な action ファイルが不要でディレクトリが綺麗になる

実務でも使ってたが、より知見が深まった気がする。