Salesforce Lightning レコードページの習得:LWCによるカスタマイズ開発者ガイド

背景と応用シナリオ

Salesforce のユーザーインターフェース (UI) が Classic から Lightning Experience (Lightningエクスペリエンス) へと移行したことで、私たちがデータをどのように表示し、操作するかは劇的に変化しました。その中心的な役割を担うのが Lightning Record Pages (Lightning レコードページ) です。これは、取引先、商談、カスタムオブジェクトなど、個々のレコードを表示するための、動的でコンポーネントベースのレイアウトです。Salesforce 開発者 (Salesforce Developer) として、私たちはこのページを単なる静的な情報表示の場としてではなく、ビジネスプロセスを加速させ、ユーザーエクスペリエンス (UX) を向上させるための強力なキャンバスとして捉えるべきです。

従来のページレイアウト (Page Layout) は、項目の配置や関連リストの表示といった基本的なカスタマイズに限定されていました。しかし、Lightning Record Pages は、Lightning App Builder (Lightning アプリケーションビルダー) という宣言的なツールを用いて、標準コンポーネント、カスタムコンポーネント、そして AppExchange から入手したコンポーネントを自由にドラッグ&ドロップで配置できます。

開発者にとっての真価は、Lightning Web Components (LWC) (Lightning Webコンポーネント) や Aura Components を用いて、独自のカスタムコンポーネントを作成し、このページに組み込める点にあります。これにより、以下のような高度な応用シナリオが実現可能になります:

シナリオ1:外部システムデータの統合表示

取引先レコードページ上に、その顧客に関連する外部ERPシステムの受注履歴や請求情報をリアルタイムで表示するカスタムLWCを配置する。これにより、ユーザーは Salesforce を離れることなく、顧客に関する360度のビューを得られます。

シナリオ2:複雑なビジネスロジックのUI実装

商談レコードページで、標準の関連リストでは表現できないような、複雑な条件分岐を持つカスタム商品選択ウィザードを LWC で構築する。例えば、選択された商品に応じて、動的に追加のオプションや関連サービスを提示するようなUIです。

シナリオ3:データの可視化とインタラクション

取引先レコードに紐づくケース(問い合わせ)データを、単なるリストではなく、ステータス別のパイチャートや月次トレンドのラインチャートとして視覚的に表示するコンポーネントを配置する。ユーザーはグラフをクリックすることで、該当するデータにドリルダウンできます。

このように、Lightning Record Page は、宣言的な設定の容易さと、プログラムによる無限の拡張性を融合させるプラットフォームであり、開発者がビジネス要件に深く応えるための重要なフィールドなのです。


原理説明

Lightning Record Page の動作原理を理解することは、効果的な開発を行う上で不可欠です。ページは基本的に、メタデータによって定義されたコンポーネントのコンテナとして機能します。

ページの構造

Lightning App Builder でページを作成または編集する際、通常はヘッダーと3つのリージョン(領域)を持つテンプレートを選択します。各リージョンに、様々なタイプのコンポーネントを配置していきます。

  • Standard Components (標準コンポーネント): Salesforce が提供する既製のコンポーネントです。「Highlights Panel (強調パネル)」、「Record Detail (レコードの詳細)」、「Related Lists (関連リスト)」などがこれにあたります。
  • Custom Components (カスタムコンポーネント): 開発者が LWC または Aura を用いて作成した独自のコンポーネントです。これが、私たちが最も力を発揮する領域です。
  • AppExchange Components (AppExchange コンポーネント): Salesforce のパートナーが開発し、AppExchange マーケットプレイスで配布しているコンポーネントです。

開発者から見たコンポーネントの連携

開発した LWC を Lightning Record Page で利用可能にするには、コンポーネントのメタデータ設定ファイル(`js-meta.xml`)で、そのコンポーネントが配置可能な場所(ターゲット)を明示的に指定する必要があります。

具体的には、`` タグ内に `lightning__RecordPage` を含めることで、その LWC が Lightning App Builder のコンポーネントリストに表示され、レコードページに配置できるようになります。

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>58.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordPage</target>
        <target>lightning__AppPage</target>
        <target>lightning__HomePage</target>
    </targets>
</LightningComponentBundle>

コンテキストの取得

レコードページに配置された LWC は、自分がどのレコードのページにいるかを知る必要があります。Salesforce はこのための仕組みを提供しています。LWC の JavaScript ファイル内で `@api` デコレータを使ってパブリックプロパティを宣言すると、Lightning Record Page は自動的にそのプロパティにコンテキスト情報を渡します。

  • `recordId`: 現在のレコードの ID を受け取ります。これは最も一般的に使用されるプロパティです。
  • `objectApiName`: 現在のレコードのオブジェクトの API 参照名(例: `Account`, `Contact`)を受け取ります。

この `recordId` を使って、Apex コントローラを呼び出し、関連データを取得したり、レコードを更新したりするのが一般的な開発パターンです。


示例代码

ここでは、取引先 (Account) のレコードページに配置し、その取引先に関連するすべてのケース (Case) を一覧表示するシンプルな LWC を作成します。この例は developer.salesforce.com の公式ドキュメントにあるパターンに基づいています。

1. Apex コントローラ (`AccountCaseController.cls`)

まず、指定された取引先 ID に紐づくケースのリストを取得する Apex メソッドを作成します。`@AuraEnabled(cacheable=true)` アノテーションは、このメソッドが LWC から呼び出し可能であること、そして結果をクライアントサイドでキャッシュできることを示し、パフォーマンスを向上させます。

// AccountCaseController.cls
public with sharing class AccountCaseController {
    /**
     * @description 指定された取引先IDに関連するケースのリストを返します。
     * @param accountId ケースを取得する対象の取引先ID
     * @return ケースのリスト
     */
    @AuraEnabled(cacheable=true)
    public static List<Case> getCasesByAccountId(Id accountId) {
        // SOQLクエリを使用して、指定されたAccountIdに紐づくケースを取得します。
        // ID, CaseNumber, Subject, Status, Priority, Originの各項目を選択します。
        // CreatedDateの降順でソートし、最大50件に制限します。
        return [
            SELECT Id, CaseNumber, Subject, Status, Priority, Origin
            FROM Case
            WHERE AccountId = :accountId
            ORDER BY CreatedDate DESC
            LIMIT 50
        ];
    }
}

2. Lightning Web Component (`relatedCasesList`)

次に、取得したデータを表示する LWC を作成します。

relatedCasesList.html

この HTML テンプレートは、コンポーネントの見た目を定義します。`lightning-card` を使って全体を囲み、`@wire` サービスからデータが取得できたら、`lightning-datatable` を使ってケースのリストを表形式で表示します。エラーが発生した場合は、エラーメッセージを表示します。

<!-- relatedCasesList.html -->
<template>
    <lightning-card title="Related Cases" icon-name="standard:case">
        <div class="slds-m-around_medium">
            <!-- @wire サービスからデータが取得された場合に、このブロックが表示されます -->
            <template if:true={cases.data}>
                <lightning-datatable
                    key-field="Id"
                    data={cases.data}
                    columns={columns}
                    hide-checkbox-column="true"
                    show-row-number-column="true">
                </lightning-datatable>
            </template>

            <!-- @wire サービスからエラーが返された場合に、このブロックが表示されます -->
            <template if:true={cases.error}>
                <p>An error occurred while loading cases:</p>
                <p>{cases.error.body.message}</p>
            </template>

            <!-- データがまだ読み込まれていない場合に、スピナーを表示します -->
            <template if:false={cases.data}>
                 <lightning-spinner alternative-text="Loading" size="small"></lightning-spinner>
            </template>
        </div>
    </lightning-card>
</template>

relatedCasesList.js

この JavaScript ファイルは、コンポーネントのロジックを制御します。`@api recordId` で現在のレコードページの ID を受け取り、`@wire` デコレータを使って Apex メソッド `getCasesByAccountId` を呼び出します。データテーブルのカラム定義もここで行います。

// relatedCasesList.js
import { LightningElement, api, wire } from 'lwc';
// Apexコントローラからメソッドをインポートします
import getCasesByAccountId from '@salesforce/apex/AccountCaseController.getCasesByAccountId';

// データテーブルのカラム定義
const COLUMNS = [
    { label: 'Case Number', fieldName: 'CaseNumber', type: 'text' },
    { label: 'Subject', fieldName: 'Subject', type: 'text' },
    { label: 'Status', fieldName: 'Status', type: 'text' },
    { label: 'Priority', fieldName: 'Priority', type: 'text' }
];

export default class RelatedCasesList extends LightningElement {
    // @apiデコレータにより、このプロパティは公開され、
    // Lightning Record Pageから現在のレコードIDが自動的に設定されます。
    @api recordId;

    // データテーブルのカラム設定をプロパティとして保持します。
    columns = COLUMNS;
    
    // @wireサービスを使用してApexメソッドを呼び出します。
    // recordIdが変更されると、このメソッドはリアクティブに再実行されます。
    // 結果は cases プロパティに格納されます (dataまたはerrorプロパティを持つオブジェクトとして)。
    @wire(getCasesByAccountId, { accountId: '$recordId' })
    cases;
}

relatedCasesList.js-meta.xml

最後に、このコンポーネントが取引先レコードページで利用可能であることを Salesforce に伝えます。

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>58.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <!-- このコンポーネントをレコードページで利用可能にします -->
        <target>lightning__RecordPage</target>
    </targets>
    <!-- どのオブジェクトのレコードページで利用できるかを指定します -->
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <objects>
                <object>Account</object>
            </objects>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

これらのファイルをデプロイした後、Lightning App Builder で取引先レコードページを開き、カスタムコンポーネントリストから「Related Cases List」をドラッグ&ドロップで配置すれば、実装は完了です。


注意事項

Lightning Record Page 上でカスタムコンポーネントを開発・運用する際には、いくつかの点に注意が必要です。

権限 (Permissions)

コンポーネントが正しく動作するためには、ユーザーが必要な権限を持っていることを確認する必要があります。上記の例では、ユーザーは `AccountCaseController` Apex クラスへのアクセス権、Case オブジェクトへの参照権限、そして Case オブジェクトの各項目(`CaseNumber`, `Subject`など)に対する Field-Level Security (項目レベルセキュリティ) での参照権限が必要です。権限が不足している場合、コンポーネントはエラーを表示するか、データを表示しません。

パフォーマンス (Performance)

1つのページに多数の複雑なカスタムコンポーネントを配置すると、ページの読み込み時間が長くなる可能性があります。Apex メソッドでは `@AuraEnabled(cacheable=true)` を積極的に使用して、サーバーへのラウンドトリップを減らしましょう。また、一度に大量のデータを取得するのではなく、ページネーションや無限スクロールを実装して、必要なデータのみを段階的に読み込むことも検討してください。

ガバナ制限 (Governor Limits)

コンポーネントから呼び出される Apex コードは、Salesforce のガバナ制限に従います。1トランザクションあたりの SOQL クエリ数(100件)、合計クエリ行数(50,000件)、CPU 時間などに注意してください。特に、ループ内で SOQL クエリを発行するようなコードは絶対に避けるべきです。

エラー処理 (Error Handling)

サンプルコードで示したように、`@wire` サービスは `data` と `error` の両方のプロパティを提供します。Apex 呼び出しが失敗した場合や、ユーザーに権限がない場合に備え、`error` プロパティを適切に処理し、ユーザーフレンドリーなエラーメッセージを表示することが重要です。これにより、問題発生時のトラブルシューティングが容易になります。


まとめとベストプラクティス

Lightning Record Page は、Salesforce 開発者にとって、標準機能とカスタム開発をシームレスに統合し、優れたユーザーエクスペリエンスを提供する強力なツールです。宣言的な設定の手軽さと、LWC によるプログラマティックな拡張性を組み合わせることで、複雑なビジネス要件にも柔軟に対応できます。

ベストプラクティス

  • 1. 宣言的アプローチを優先する: カスタムコードを書く前に、Dynamic Forms (動的フォーム)Dynamic Actions (動的アクション)、コンポーネントの表示ルールなど、Lightning App Builder の標準機能で要件を満たせないか常に検討してください。コードは、標準機能では不可能なことを実現するための手段です。
  • 2. コンポーネントの再利用性を意識する: 特定のオブジェクトやレコードに固執しない、汎用的なコンポーネントを設計するよう心がけましょう。例えば、`objectApiName` を `@api` プロパティとして受け取り、オブジェクトに応じて動的に動作を変えるコンポーネントは、再利用性が高まります。
  • 3. 関心の分離: LWC は UI の表示とユーザーインタラクションに集中させ、複雑なデータ処理やビジネスロジックは Apex クラスに任せましょう。この原則に従うことで、コードの保守性とテストの容易性が向上します。
  • 4. ユーザーエクスペリエンスを考慮した設計: ページに情報を詰め込みすぎないように注意してください。タブ (Tabs) やアコーディオン (Accordion) コンポーネントを活用して情報を整理し、ユーザーが必要な情報に素早くアクセスできるように設計することが重要です。ページのパフォーマンスも UX の重要な要素です。

Lightning Record Page をマスターすることは、現代の Salesforce 開発者にとって必須のスキルです。その原理を深く理解し、ベストプラクティスに従うことで、ビジネスの価値を最大化する、効率的で使いやすいアプリケーションを構築できるでしょう。

コメント