Angularでのrxjsを用いたモーダル共通化

# はじめに
Angularに標準で入っているrxjsライブラリを使用してモーダルを共通化することができます！
一つだけモーダルを作っておき、あとはメソッドを呼び出して表示したいタイトル、内容を引数に渡すだけです！
最終的な成果物は動画↓のようになります
![rxjsモーダル.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/748651/b934259c-a187-a235-1574-da48ad754c4a.gif)

# 1. モーダルの状態を司るサービスの作成

```ts:src/app/services/modal.service.ts
import { Injectable } from '@angular/core';
import { Subject } from 'rxjs';

@Injectable({
  providedIn: 'root',
})
export class ModalService {
  private _subject: Subject<{ title: string; body: string } | null> =
    new Subject();
  constructor() {}

  get subject() {
    return this._subject;
  }

  openModal(title: string, body: string) {
    this._subject.next({ title, body });
  }

  closeModal() {
    this._subject.next(null);
  }
}
```

# 2. モーダルコンポーネントの作成

```html:src/app/components/modal.component.html
<div *ngIf="active" class="grayout">
  <main>
    <header>{{ title }}</header>
    <section>{{ body }}</section>
    <footer>
      <button (click)="closeModal()">OK</button>
    </footer>
  </main>
</div>
```
```scss:components/modal.component.scss
.grayout {
  position: fixed;
  top: 0;
  left: 0;
  background-color: #00000050;
  width: 100vw;
  height: 100vh;
  display: flex;
  align-items: center;
  justify-content: center;
  main {
    width: 500px;
    height: 500px;
    background-color: #ffffff;
    border-radius: 4px;
    display: flex;
    flex-direction: column;
    header {
      text-align: center;
      margin: 0;
      border-radius: 4px 4px 0 0;
      color: #ffffff;
      background-color: #1976d2;
      height: 40px;
      line-height: 40px;
    }
    section {
      flex: 1;
      padding: 15px;
    }
    footer {
      height: 40px;
      display: flex;
      padding: 15px;
      button {
        margin-left: auto;
      }
    }
  }
}
```

```ts:src/app/components/modal.component.ts
import { Component, OnInit, OnDestroy } from '@angular/core';
import { ModalService } from 'src/app/services/modal.service';
import { Subscription } from 'rxjs';

@Component({
  selector: 'app-modal',
  templateUrl: './modal.component.html',
  styleUrls: ['./modal.component.scss'],
})
export class ModalComponent implements OnInit, OnDestroy {
  active = false; // モーダルの表示、非表示を切り替える変数
  title = '';
  body = '';
  subscription: Subscription;

  constructor(private modalService: ModalService) {
    // subscribeで購読し、タイトル＆内容が流れてきた時はモーダルを表示。空の場合は非表示
    this.subscription = this.modalService.subject.subscribe((next) => {
      if (next) {
        this.active = true;
        this.title = next.title;
        this.body = next.body;
      } else {
        this.active = false;
        this.title = '';
        this.body = '';
      }
    });
  }

  ngOnInit(): void {}

  ngOnDestroy(): void {
    this.subscription.unsubscribe();
  }

  closeModal() {
    this.modalService.closeModal();
  }
}
```

# 3. モーダル呼び出し側の作成
```html:src/app/pages/about.component.html
<button (click)="openModal1()">モーダルを開く1</button>
<button (click)="openModal2()">モーダルを開く2</button>
<button (click)="openModal3()">モーダルを開く3</button>
```

```ts:src/app/pages/about.component.ts
import { Component, OnInit } from '@angular/core';
import { ModalService } from 'src/app/services/modal.service';

@Component({
  selector: 'app-about',
  templateUrl: './about.component.html',
  styleUrls: ['./about.component.scss'],
})
export class AboutComponent implements OnInit {
  constructor(private modalService: ModalService) {}

  ngOnInit(): void {}

  // モーダルを表示したい場合はモダールサービスのopenModalメソッドを呼び出すだけ！
  openModal1() {
    this.modalService.openModal('タイトル１', '内容１');
  }
  openModal2() {
    this.modalService.openModal('タイトル２', '内容２');
  }
  openModal3() {
    this.modalService.openModal('タイトル３', '内容３');
  }
}

```