リストビューから選択したレコードを保持してLWCにリダイレクトするVFコンポーネント

## はじめに

Salesforceにて複数のレコードを対象としてアクションを行いたい場合、多くの場合リストビューからレコードにチェックを入れて、その選択情報を引き継いでカスタムアクションを起動したくなります。

リストビューボタンから選択レコードを保持して画面遷移を行う場合には、Visualforceか画面フローを経由する必要があります。
画面フローで済む場合にはそれで全く構わないと思っています。[こちらの記事](https://qiita.com/kata73467963/items/47ed773656c3907bf5c9)が大変参考になるので是非ご参照ください。

問題は画面フロー以上に作りこんだ機能を実現したい場合です。今の時代、Salesforce上に作りこんだ画面を開発する場合はLWC (Lightning Web Components) を使って開発したいですが、リストビューボタンから選択レコードの情報を保持して直接LWCに遷移する方法は現状ありません (2025年3月現在、[IdeaExchangeではOn Roadmapとして上がっている](https://ideas.salesforce.com/s/idea/a0B8W00000QO2m8UAD/add-lwc-as-custom-button-for-related-list-and-list-view))。

そこで、Visualforceを最小限だけ書いてLWCに橋渡しするためのVisualforceコンポーネントを作ります。

## できあがり

### Visualforceコンポーネント

このコンポーネントをリストビューから呼び出すVFページで再利用します。

ListButtonRedirect.component

```html 
<apex:component>
  <apex:attribute
    name="records"
    type="SObject[]"
    description="Records selected in list view."
    required="true"
  />
  <apex:attribute
    name="path"
    type="String"
    description="Redirect path to new page."
  />
  <apex:attribute
    name="paramKey"
    type="String"
    description="Query parameter key of selected record Ids."
    default="c__recordIds"
  />
  <apex:attribute
    name="delimiter"
    type="String"
    description="Delimiter of selected record Ids."
    default=","
  />
  <apex:outputPanel rendered="{!NOT(ISBLANK(path))}" layout="none">
    <script type="module">
      const paramKey = '{!paramKey}';
      const recordIds = [
        <apex:repeat value="{!records}" var="r">
          '{!r.Id}',
        </apex:repeat>
      ];
      const url = new URL('{!path}', 'base://base');
      if (paramKey.length && recordIds.length) {
        url.searchParams.set(paramKey, recordIds.join('{!delimiter}'));
      }
      sforce.one?.navigateToURL(url.pathname + url.search, true);
    </script>
  </apex:outputPanel>
</apex:component>
```

### Visualforceページ

各リストビューアクションでこんな感じにコンポーネントを使います（これだけです）。
変更するのは`standardController`のオブジェクト名、`path`のリダイレクト先のみです。
LWCを遷移先にする場合は、[URL Addressable](https://developer.salesforce.com/docs/platform/lwc/guide/use-navigate-url-addressable.html)にするか[Lightningコンポーネントタブ](https://developer.salesforce.com/docs/platform/lwc/guide/use-config-custom-tab-intro.html)に設定してpathを変更してください。

ListViewPage.page

```html
<apex:page standardController="Account" recordSetVar="records">
  <c:ListButtonRedirect
    records="{!selected}"
    path="/lightning/cmp/c__someLwc"
  />
</apex:page>
```

### リストビューボタンを作成・配置

上記のVisualforceページを作成したら、リストボタンの遷移先として設定しリストビューボタンレイアウトに配置します。

![リストボタン作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4047729/6b58ddf5-49e9-48bc-998d-27d71606061b.png)

![リストボタン配置.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4047729/dd53ec6d-a0b2-40d4-8824-ac58a19dbdd8.png)

### 動作確認

リストボタンから選択したレコードのIDをクエリパラメータに保持してLWCに遷移します。ここまでくればLWC側で煮るなり焼くなり好きにすればOKです。

![リストボタン.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4047729/cab61243-9177-4ddd-afc5-19c189e8d4d8.png)

![遷移先LWC.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/4047729/0439738e-2c20-4d92-814c-991de1722c1a.png)

## ちょっとした解説

リストビューボタンに設定するVFページでは、`StandardSetController`を使用します。標準コントローラーを使っているページでは、getterメソッドが用意されているプロパティについては、基本的にプロパティ名を直接書くことでVFページ内から直接アクセスできます。そこで、選択レコードの情報を`{!selected}`としてコンポーネントに渡しています。

コンポーネント側では、その選択レコードの配列を`apex:repeat`でJavaScriptに展開して、[`sforce.one`の機能](https://developer.salesforce.com/docs/atlas.ja-jp.salesforce1.meta/salesforce1/salesforce1_dev_jsapi_sforce_one.htm)からリダイレクトしています。
ちなみに、コンポーネントではレコードIDのパラメータキーとデリミタが指定可能になっています。