LMS (Lightning Message Service)を使ってみた

# 背景
LMS (Lightning Message Service）を利用してみたいという気持ちはずっとありましたが、Parent-Child間でほぼ済んでいたので、無関係なコンポーネント間のデータのやり取りニーズは正直ありませんでした。

今回その必要性が出てきたのですが、その前に、標準の関連リストが簡単でお気に入りである一方、どうしても一括編集などができないのでlightning-datatableのonrowactionやdraft-valuesを利用した一括編集のできる関連リスト（LWC）をカスタムで作成していました。

そこで、さらにQuickActionからFlowを起動して複数の関連レコードを作成する機会があり、QuickActionからフローを起動してレコードを作成したのですがここで問題が発生です。

標準リストでは新規に作成したレコードも含めてリストを自動で更新しますが、カスタム関連リストでは更新しません。これは、なにかしらLWCヘ新規レコードが作成されたということを通知する必要があり、LMSで解決できそうではないかと思ったわけです。

# 結果
結果は、LMSでFlowのステータスが「FINISHED」が帰ってきた時にpublishし、カスタム関連リストのLWCにその旨を通知して、subscribe側のLWCで再度情報を取得するメソッドを走らせることで、リストが無事更新されることになりました。標準関連リストに寄せてカスタムを作成すると大変だなぁという感想です。

ただこのLMSは取得したデータをどんな型でも送れるので、あるコンポーネントのデータ群を親子関係でない別のコンポーネントへ送って描画する場合にも利用できるのではないかと考えています。

# デモ
1. LWCを起動する`Quick Contact`のQuick Actionボタンを押すと、LWCのModalが出現して、データをInputします。（Screen Flow)
1. Flowを完了するとlightning-flowのonstatuschangeで、`FINISHED`のステータスがLWCに戻ってきて、MessageChannelでpublishします。
1. MessageChannelを通じて別のLWCが通知をsubscribeして、`データ更新`のメソッドを走らせます。今回はshowToastでSubscribedした旨も通知です。

![subscribe.gif](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15524/1fd809d5-c204-0c28-0363-b555ce033bda.gif)

# 構成
Screen Flow * 1
Quick Action * 1
Class * 1
LWC * 2
Message Channel * 1
以上です。

# 手順
### 1. Flowを内包したLWCを作成
```
<template>
    <lightning-card title={title} icon-name={iconName}>
        <div class="slds-m-top_small">
            <lightning-flow lwc:if={recordId} flow-api-name={flowName} flow-input-variables={inputVariables}
                onstatuschange={handleFlowFinish}></lightning-flow>
        </div>
    </lightning-card>
</template>
```

### 2. QuickActionからはLWC経由でフローを起動
Flow API Name はこちらを参照
![Screenshot 2024-01-13 at 16.52.02.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15524/fc32f555-7adf-175f-e993-6be5eb01fe93.png)
Screen Flowで入力画面から値を代入
![Screenshot 2024-01-13 at 16.53.19.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15524/1d8e76d4-a40f-7609-1912-b90e633e74f9.png)

:::note warn
recordIdはFlow外部からの入力を可にします。LWCからinputVariablesとして入ってくるIdを受け止めるためです。
![Screenshot 2024-01-14 at 11.59.56.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15524/7fe56555-f264-7e5b-1028-c94ef01d4719.png)

:::
### 3. Flowが終わったらフローのステータスを取得
```
  handleFlowFinish(event) {
    if (event.detail.status === "FINISHED") {
      const message = {
        recordId: this.recordId,
        status: event.detail.status,
      };
      this.dispatchEvent(new CloseActionScreenEvent());

      // Publish the message
      publish(this.messageContext, FINISH_FLOW_CHANNEL, message);
    }
  }
```

:::note warn
recordIdですが、通常は`@api recordId`で取得できますが、今回はScreen Action (Ui action)なので、renderedされて初めて取得されます。よってgetter, setterで設定します。と同時にinputVariablesにそのrecordIdを取得後挿入して、lightning-flowを起動します。
```
  inputVariables = [];

  _recordId;
  @api
  get recordId() {
    return this._recordId;
  }

  set recordId(value) {
    if (value !== this._recordId) {
      this._recordId = value;
      this.inputVariables = [
        {
          name: "recordId",
          type: "String",
          value: value,
        },
      ];
    }
  }
```
:::
### 4. LMSにmessageを送信
上記と合わせてpublishするためにjsにimportしておきます。
Publish側 (createContactQuickAction)
```
import { publish, MessageContext } from "lightning/messageService";
import FINISH_FLOW_CHANNEL from "@salesforce/messageChannel/finishFlow__c";
```

:::note warn
**messageChannels**
VS Codeの**force-app/main/default/** 下に手動でmessageChannelsというfolderを作成して、その中にmessageChannel-meta.xmlを登録する必要があります。
![Screenshot 2024-01-13 at 18.13.50.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/15524/d21b634b-70dc-d9a9-b1e9-e14f963764eb.png)

:::

### 5. カスタム関連リストがLMS経由でpublisherのmessageを受け取る
Subscribe側 (getContactList)
```
import { subscribe, MessageContext } from "lightning/messageService";
import FINISH_FLOW_CHANNEL from "@salesforce/messageChannel/finishFlow__c";

```
### 6. 情報取得methodを動かして更新
messageが来るのを`@wire`で監視します。
```
  @wire(MessageContext)
  messageContext;
  subscription = null;
```
```
  subscribeToChannel() {
    // Subscribe to the message channel
    if (this.subscription) {
      return;
    }
    this.subscription = subscribe(
      this.messageContext,
      FINISH_FLOW_CHANNEL,
      (message) => this.handleMessage()
    );
  }

  // Subscribing to the message channel debug
  handleMessage() {
    this.handleGetContacts(); // to refresh the datatable
    const params = {
      message: "Subscribed Successfully",
      mode: "dismissable",
      variant: "success",
    };
    this.handleShowToast(params);
  }
```
  
# 参照
[Use Quick Actions - Screen Flow  (Salesforce)](https://developer.salesforce.com/docs/platform/lwc/guide/use-quick-actions-screen.html)
[Lightning Flow (Salesforce)](https://developer.salesforce.com/docs/component-library/bundle/lightning-flow/documentation)
[Lightning Datatable (Salesforce)](https://developer.salesforce.com/docs/component-library/bundle/lightning-datatable/documentation)
[LMS Component Library (Salesforce)](https://developer.salesforce.com/docs/component-library/bundle/lightning-message-service/documentation)
[Lightning Message Service（LMS）を触ってみました (TerraSky)](https://base.terrasky.co.jp/articles/etZAC)

# まとめ
これは以前の投稿した[商品の値段や個数を自由に変更できる「商品追加ボタン」を作ってみた (lwc)](https://qiita.com/masahiro_sf/items/a53ee70671efb8891740)での商談商品の再取得にも利用できそうです。

標準ではFlowで作成した新規はすぐに関連リストに表示されます。Inline Editをさせてくれれば最高なんですが、それゆえにListEditorとかの利用需要があるものわかります。

自動更新は、Master-detailの集計項目があればそれを`@wire`で絡めて、新規データが追加されるごとのデータ取得を走らせることもできます。今回は単にLookup関係でもできるようにしました。

難易度的には簡単な方から：
1. `related list`
1. `mater-detail relationship with @wire`
1. `Lightning Message Service`
でした。

Lightning Message Serviceを学べてとても楽しかったです。
githubにコードを公開しておきますので、もしアイディアのある方がいたらぜひ。
https://github.com/liuxiachanghong/lightning-message-service