【LWC】Lightning Message Service使ってみた

#Lightning Message Serviceとは
Lightningページ内でDOM全体の通信を行うものです。LWCは複数のコンポーネントを使用してページを構成することが多いですが、その時にコンポーネント間でPub-Sub方式で通信をすることが出来ます。ちなみにLWCだけでなく、VisualforceやAuraコンポーネントでも通信が出来ますが今回はLWC間で通信を行います。
#作成するもの
今回作成するものは取引先をクリックしたら、その取引先の取引先責任者を表示するものです。1画面であればなんてことないものですが、２つのコンポーネントに跨る場合はMessage Serviceが必要になります。
####初期画面
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407598/40b1ddd0-b9a6-f59b-71c6-821d1dfcd090.png)
####一番上の取引先をクリック
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/407598/c0769f80-b215-5884-a151-a6575472ca74.png)
右のコンポーネントに取引先に紐づく取引先責任者が表示される。

#メッセージチャネルの作成
Lightning Message Serviceを使用するにはLightningMessageChannelメタデータ型を定義する必要があるそうです。force-app/main/defaultにmessageChannelsディレクトリを作成し、xmlファイルを作成します。

```xml:SampleMessageChannel.messageChannel-meta.xml
<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
    <description>This is a sample Lightning Message Channel for the Lightning Web Components.</description>
    <isExposed>true</isExposed>
    <lightningMessageFields>
        <description>This is the record Id that changed</description>
        <fieldName>recordId</fieldName>
    </lightningMessageFields>
    <masterLabel>SampleMessageChannel</masterLabel>
</LightningMessageChannel>
```
masterLabelに任意の名前を入力します。

#取引先のリスト
まずは左側のコンポーネントです。こちらはメッセージを公開する側です。

```html:accountList.html
<template>
    <lightning-card title="Account List" icon-name="custom:custom1">
      <div class="slds-m-around_medium" onaccountselect={handleAccountSelect}>
        <template if:true={account.data}>
          <template for:each={account.data} for:item="acc">
            <c-account-list-item key={acc.Id} account={acc}></c-account-list-item>
          </template>
        </template>
      </div>
    </lightning-card>
</template>
```
```javascript:accountList.js
import { LightningElement, wire } from 'lwc';
import getAccountList from '@salesforce/apex/AccountTableViewController.getAccountList';
import { publish, MessageContext } from 'lightning/messageService';
import SampleMC from '@salesforce/messageChannel/SampleMessageChannel__c';

export default class AccountList extends LightningElement {
  @wire(getAccountList)
  account;

  @wire(MessageContext)
  messageContext;

  handleAccountSelect(event) {
    console.log('select');
    const payload = { recordId: event.target.account.Id };
    publish(
      this.messageContext, 
      SampleMC, 
      payload
    )
  }
}
```
下記の2行でpublishとMessageContextと、先ほど作成したメタデータをimportします。作成したメタデータファイル名の最後に「__c」をつけないといけないらしいです。

```javascript
import { publish, MessageContext } from 'lightning/messageService';
import SampleMC from '@salesforce/messageChannel/SampleMessageChannel__c';
```
@wire(MessageContext)でMessageContextオブジェクトを作成します。payloadが今回右側のコンポーネントに渡す取引先のIdになります。ここは形式を覚えておけば大丈夫です。
#取引先責任者のリスト
こちらはメッセージを受け取る側のコンポーネントです。

```javascript:contactDetail.js
import { LightningElement, wire } from 'lwc';
import getContacts from '@salesforce/apex/ContactHandler.getContacts';
import { subscribe, unsubscribe, APPLICATION_SCOPE, MessageContext } from 'lightning/messageService';
import SampleMC from '@salesforce/messageChannel/SampleMessageChannel__c';

export default class ContactDetail extends LightningElement {
  subscription = null;
  recordId;

  @wire(getContacts, {accId : '$recordId'})
  contacts;

  @wire(MessageContext)
  messageContext;

  // messageを購読
  subscribeToMessageChannel() {
    if(!this.subscription) {
      this.subscription = subscribe(
        this.messageContext,
        SampleMC,
        (message) => this.recordId = message.recordId,
        {scope: APPLICATION_SCOPE}
      );
    }
  }

  unsubscribeToMessageChannel() {
    unsubscribe(this.subscription);
    this.subscription = null;
  }

  connectedCallback() {
    this.subscribeToMessageChannel();
  }

  disconnectedCallback() {
    this.unsubscribeToMessageChannel();
  }
}
```
受け取る側では下記2行をインポートします。

```javascript
import { subscribe, unsubscribe, APPLICATION_SCOPE, MessageContext } from 'lightning/messageService';
import SampleMC from '@salesforce/messageChannel/SampleMessageChannel__c';
```
connectedCallbackでsubscribeToMessageChannelを呼び出し、メッセージを購読します。今回の場合はrecordIdですのでmessage.recordIdをthis.recordIdに代入します。あとは@wireを使用して取引先責任者のレコードを取得して表示するだけです。

Pub-Sub方式になじみがなかったのではじめは戸惑いましたが、メッセージのやり取りのイメージさえ掴めればそこまで難しくないです。どの程度使用する機会があるか分かりませんが、使い道はある気がします。

ソースコード：https://github.com/KZ63/Salesforce

#参考
https://developer.salesforce.com/docs/component-library/documentation/ja-jp/lwc/lwc.use_message_channel