🌩️

[Salesforce] LightningModalを使ったモーダル実装

2023/11/29に公開

TERASSでSalesforceをやったりやらなかったりしているimslpです。

今回はWinter23で追加されたLWCのモーダルコンポーネントを今さら使ってみます。

前までモーダルを実装する時はSLDSを使った実装をしていたと思います。
SLDSドキュメントからの引用ですがこんな感じです。

<section role="dialog" tabindex="-1" aria-modal="true" aria-labelledby="modal-heading-01" class="slds-modal slds-fade-in-open">
  <div class="slds-modal__container">
    <button class="slds-button slds-button_icon slds-modal__close slds-button_icon-inverse">
      <svg class="slds-button__icon slds-button__icon_large" aria-hidden="true">
        <use xlink:href="/assets/icons/utility-sprite/svg/symbols.svg#close"></use>
      </svg>
      <span class="slds-assistive-text">Cancel and close</span>
    </button>
    <div class="slds-modal__header">
      <h1 id="modal-heading-01" class="slds-modal__title slds-hyphenate">Modal header</h1>
    </div>
    <div class="slds-modal__content slds-p-around_medium" id="modal-content-id-1">
      <p>Sit nulla est ex deserunt exercitation anim occaecat. Nostrud ullamco deserunt aute id consequat veniam incididunt duis in sint irure nisi. Mollit officia cillum Lorem ullamco minim nostrud elit officia tempor esse quis. Cillum sunt ad dolore quis
        aute consequat ipsum magna exercitation reprehenderit magna. Tempor cupidatat consequat elit dolor adipisicing.</p>
      <p>Dolor eiusmod sunt ex incididunt cillum quis nostrud velit duis sit officia. Lorem aliqua enim laboris do dolor eiusmod officia. Mollit incididunt nisi consectetur esse laborum eiusmod pariatur proident. Eiusmod et adipisicing culpa deserunt nostrud
        ad veniam nulla aute est. Labore esse esse cupidatat amet velit id elit consequat minim ullamco mollit enim excepteur ea.</p>
    </div>
    <div class="slds-modal__footer">
      <button class="slds-button slds-button_neutral" aria-label="Cancel and close">Cancel</button>
      <button class="slds-button slds-button_brand">Save</button>
    </div>
  </div>
</section>
<div class="slds-backdrop slds-backdrop_open" role="presentation"></div>

引用元:
https://www.lightningdesignsystem.com/components/modals/#Base

クラス名を指定する方法は個人的にメンドクサイしなんとなく可読性が低い気がします。
これがLightningModalを使うことでどのように記述できるのか見ていきます。
詳しいことはすべてドキュメントに書いてあります。
https://developer.salesforce.com/docs/component-library/bundle/lightning-modal/documentation

モーダル側の実装(JS)

myModal.js
import LightningModal from 'lightning/modal';

export default class MyModal extends LightningModal {
  handleCancel() {
    // キャンセル時処理
  }
  handleOkay() {
    // OKボタン、送信ボタン時処理
  }
}

基本となる実装はこれです。
LigtningModalをImportし、extendsしています。
もちろん、Navigationを使う時はextends NavigationMixin(LightningModal)のようにできます。

モーダルを閉じる時はthis.close();のように記述します。this.close('cancel');と引数を入れればモーダルの起動元に文字列を返却することができます。オブジェクトも返却できそうだったので今回はオブジェクトを返却しています。

ではデータを受け取る準備など、ちょこっとだけ変更します。

myModal.js
import LightningModal from "lightning/modal";
+import { api } from "lwc";

export default class MyModal extends LightningModal {
+ @api record;

  handleCancel() {
    // キャンセル時処理
+   const result = { status: "cancel" };
+   this.close(result);
  }
  handleOkay() {
    // OKボタン、送信ボタン時処理
+   const result = { status: "success", record: this.record };
+   this.close(result);
  }
}

準備は完了です。次はHTMLを見ていきます。

モーダル側の実装(HTML)

myModal.html
<template>
  <lightning-modal-header label={label}></lightning-modal-header>
  <lightning-modal-body>
    <div>Name:{record.fields.Name.value}</div>
  </lightning-modal-body>
  <lightning-modal-footer>
    <lightning-button label="Cancel" onclick={handleCancel}></lightning-button>
    <lightning-button variant="brand" label="OK" onclick={handleOkay}></lightning-button>
  </lightning-modal-footer>
</template>

lightning-modal-xxxというコンポーネントを使っています。最初と比べると簡潔ではないでしょうか?
labelは起動元の方で出てきますが、モーダルを起動するopenメソッドで指定できるプロパティです。

起動元の実装(JS)

myApp,js
import { LightningElement, api, wire } from "lwc";
import { getRecord } from "lightning/uiRecordApi";
import MyModal from "c/myModal";

export default class MyApp extends LightningElement {
  @api recordId;

  @wire(getRecord, { recordId: "$recordId", fields: ["Account.Name"] })
  wiredAccount;

  async openModal() {
    const result = await MyModal.open({
      size: "small",
      label: "Sample Modal",
      record: this.wiredAccount.data
    });

    console.log(JSON.stringify(result));
  }
}

openModal関数がメインです。Importしたモーダルコンポーネントでopenメソッドを起動しています。openメソッドには標準でいくつかのプロパティを含めることができ、任意のプロパティも追加できます。この例でいうとsize,labelは標準プロパティで、recordはカスタムプロパティです。
openメソッドのレスポンスにはさっき返すことにしたオブジェクトが入っています。

それ以外の箇所は今回特に関係ないですが、getRecord APIを使ってレコードを取得しています。

起動元の実装(HTML)

myApp.html
<template>
  <lightning-button label="Open Modal" onclick={openModal}></lightning-button>
</template>

ここは作ったopenModal関数が呼べればなんでもOKです。

完成

コードはかけたので実際の画面を見てみます。
今回は取引先のレコード詳細画面においてみます。

クリック

クローズ時のレスポンスもちゃんと帰ってきています。

{"status":"success","record":{"apiName":"Account","childRelationships":{},"fields":{"Name":{"displayValue":null,"value":"サンプル会社"}},"id":"xxx","lastModifiedById":"xxx","lastModifiedDate":"2023-11-27T05:58:40.000Z","recordTypeId":"xxx","recordTypeInfo":{"available":true,"defaultRecordTypeMapping":true,"master":false,"name":"法人取引先 レコードタイプ","recordTypeId":"xxx"},"systemModstamp":"2023-11-27T05:58:40.000Z"}}

まとめ

モーダルを実装する時はいつもコピペから始めていたんですが、これを使うことによってコピペから解放されそうです。
しかし、ある程度自由度は下がってしまうので要件と照らし合わせて使い分けていくことが大切だと思います。(LWCで用意されたコンポーネントはだいたいそうですし、特に不便を感じたタイミングは今のところ無いですが)

以上です、ご覧いただきありがとうございました。

Terass Tech Blog

Discussion