君は心理学者なのか?

大学時代に心理学を専攻しなぜかプログラマになった、サイコ(心理学)プログラマかろてんの雑記。

Angular4にて、サービスを介してコンポーネントからコンポーネントへ値を送る

コンポーネントから別のコンポーネントに値を送る

親子関係があるときは、@Input()を使用することにより親から子へ値を送りました。

今回、あるコンポーネントから親子関係にないコンポーネントへ、

サービスを介して値を送る実装をしてみたいと思います。

解説

具体的な手順に入る前に、大体の流れを確かめておきましょう。

今回やりたいことを図にすると、こんな感じになります。

f:id:karoten512:20170929160242p:plain

Serviceを介して、ComponentAからComponentBへMessageを送る感じです。

これを実現するために、まずServiceクラス内に「privateの口」「publicの耳」を用意します。

f:id:karoten512:20170929160427p:plain

次に、ComponentBにpublicの耳を配置します。

この時、耳を澄ませておくことが重要です(subscribe)

f:id:karoten512:20170929160902p:plain

これで準備が整いました。

まず、ComponentAがServiceに以下のように依頼します。

f:id:karoten512:20170929160907p:plain

次に、Serviceがこんにちは!と叫びます。

f:id:karoten512:20170929161014p:plain

そうすると、耳を澄ませていたComponentBに聞こえます。

f:id:karoten512:20170929161101p:plain

なにいってんだこいつ、という感じだと思いますので、

実際にコードを見て確認してみましょう。

0. 準備

新規プロジェクトを作成し、サーバをスタートさせます。

ng new send-value
ng serve

1. componentA, componentB, SendValueServiceを作成

ng g component comp-a
ng g component comp-b
ng g service send-value

2. SendValueServiceにてSubjectを読み込む

// send-value.service.ts
import { Subject } from 'rxjs/Subject';

3. SendValueServiceにprivateの口を追加

// send-value.service.ts
private toCompBDataSource = new Subject<string>();

4. SendValueServiceにpubilcの耳を追加

// send-value.service.ts
public toCompBData$ = this.toCompBDataSource.asObservable();

5. app.moduleににSendValueServiceをDIする

// app.module.ts
import { SendValueService } from './send-value.service'; // 追加

@NgModule({
  providers: [SendValueService], // 追加
}) 

SendValueServiceをDIするのはapp.moduleのみです。

compA, compBにもDIしてしまうと、

3つの別々のSendValueServiceインスタンスが出来てしまうので、うまく動きません。

サービスを使ってコンポーネント間で値のやり取りをするときは、

「同一のServiceインスタンス」を介して値のやり取りができるように注意しましょう。

今回はmoduleにDIしていますが、2つのコンポーネントが同一の親を保つ場合は、

その親にDIしてもOKです。

6. compBのコンストラクタにSendValueServiceを記述

// comp-b.component.ts
import { SendValueService } from '../send-value.service'; // 追加

export class CompBComponent implements OnInit {
  constructor(
    private sendValueService :SendValueService // 追加
  ) { } 

  ngOnInit() {
  }
}

7. pubilcの耳を澄まさせる(subscribe)

// comp-b.component.ts
import { Subscription } from 'rxjs/Subscription'; // 追加

export class CompBComponent implements OnInit {
  private subscription: Subscription; // 追加

  constructor(
    private sendValueService :SendValueService
  ) {

  }
 
  ngOnInit() {
    /** ここから */
    this.subscription = this.dataService.toCompBData$.subscribe(
      msg => {
        console.log('compBは' + msg + 'をうけとりました');
      });
    }
    /** ここまで追加 */
}

8. SendValueServiceに喋りを実行するメソッドを追加

// send-value.service.ts
  public sendMessageToCompB(msg)
  {
    this.toCompBDataSource.next(msg); // 喋る
  }

9. compAのコンストラクタにSendValueServiceを記述し、喋らせるメソッドを追加

// comp-a.component.ts
import { SendValueService } from '../send-value.service';

export class CompAComponent implements OnInit {

  constructor(
    private sendValueService :SendValueService // 追加
  ) { } 

  /** ここから */
  onClick() :void
  {
    this.sendValueService.sendMessageToCompB('こんにちは!');
  }
  /** ここまで追加 */
}

10. componentAのtemplateにボタンを追加

// comp-a.component.html
<button (click)="onClick()">クリックしてね</button>

結果

f:id:karoten512:20170929164624g:plain