Salesforce LWCマスターガイド:Lightning ExperienceにおけるカスタムUI構築術

背景と応用シナリオ

Salesforce Lightning Experience (ライトニングエクスペリエンス) は、最新の、生産性の高いユーザーインターフェースを提供し、多くの企業で標準となっています。その豊富な標準機能に加え、ビジネスプロセスに特化した独自のユーザー体験を構築する必要性がますます高まっています。ここで中心的な役割を果たすのが、Lightning Web Components (LWC) (ライトニングWebコンポーネント) です。

LWC は、HTML、最新の JavaScript (ECMAScript)、CSS といった Web 標準技術をベースにした Salesforce 独自の UI フレームワークです。これにより、開発者は Salesforce プラットフォーム上で、パフォーマンスが高く、再利用可能なコンポーネントを効率的に構築できます。

応用シナリオは多岐にわたります。例えば、以下のようなケースで LWC は絶大な効果を発揮します。

  • 取引先レコードページに、関連する「進行中の商談」と「完了したケース」を一つのカスタムコンポーネントでまとめて表示する。
  • 外部の在庫管理システムと連携し、商品レコードページにリアルタイムの在庫情報を表示する。
  • 複雑な見積もり計算ロジックを実装したカスタムフォームを作成し、ユーザーの入力ミスを減らし、プロセスを自動化する。

この記事では、Salesforce 開発者の視点から、取引先レコードページに関連する取引先責任者の一覧を表示するという、実践的なシナリオを通じて LWC の基本的な構築方法を解説します。


原理説明

LWC のアーキテクチャは、Web Components というブラウザネイティブの技術標準に基づいています。これにより、従来の Aura Components (オーラコンポーネント) よりも軽量で高速な動作が実現されています。LWC の開発を理解する上で、以下の主要な概念を把握することが重要です。

コンポーネントバンドル

一つの LWC は、複数のファイルから構成される「バンドル」として管理されます。

  • HTML (.html): コンポーネントの構造を定義するテンプレートファイルです。
  • JavaScript (.js): コンポーネントのロジック、データ処理、イベントハンドリングを記述するファイルです。
  • XML (.js-meta.xml): コンポーネントのメタデータを定義する設定ファイルです。これにより、コンポーネントを Lightning アプリケーションビルダーでどこに配置できるかなどを指定します。
  • CSS (.css): コンポーネントに固有のスタイルを定義します (任意)。

リアクティブ性

LWC はリアクティブなフレームワークです。JavaScript ファイル内のプロパティの値が変更されると、そのプロパティを参照している HTML テンプレートの部分が自動的に再レンダリングされます。このリアクティブ性を制御するために、デコレータと呼ばれる特別なアノテーションを使用します。

  • @api: プロパティを公開 (public) し、親コンポーネントから値を受け取れるようにします。例えば、レコードページの ID を受け取る際に使用します。
  • @wire: Salesforce のデータをリアクティブに読み込むための強力な仕組みです。Apex メソッドや Lightning Data Service (LDS) と連携し、データの取得やキャッシュ管理を宣言的に行います。
  • @track: 以前はプライベートなプロパティの変更を追跡するために使用されていましたが、現在は基本的に不要です。オブジェクトや配列の内部要素の変更を検知させたい場合に限定的に使用します。

Apex との連携

複雑なサーバーサイドロジックや、SOQL/SOSL を用いたデータ操作が必要な場合、LWC は Apex クラスのメソッドを呼び出すことができます。@wire デコレータを使用する方法が最も一般的で、データを読み取り専用で取得する場合に最適です。この方法では、クライアント側のキャッシュが自動的に管理されるため、パフォーマンスが向上します。


示例コード

ここでは、取引先 (Account) のレコードページに配置し、その取引先に関連する取引先責任者 (Contact) のリストを表示する LWC を作成します。

1. Apex コントローラーの作成

まず、サーバーサイドで取引先責任者のリストを取得するための Apex クラスを作成します。

ContactController.cls

public with sharing class ContactController {
    // @AuraEnabled アノテーションを付与することで、LWC からこのメソッドを呼び出せるようになります。
    // cacheable=true は、クライアント側で結果をキャッシュすることを許可し、@wire サービスで呼び出すための必須条件です。
    @AuraEnabled(cacheable=true)
    public static List<Contact> getContacts(Id accId) {
        // with security_enforced を使用して、項目レベルのセキュリティとオブジェクト権限を適用します。
        // これにより、ユーザーが表示権限を持たない項目やオブジェクトにアクセスしようとするとエラーが発生します。
        return [
            SELECT Id, Name, Title, Phone, Email
            FROM Contact
            WHERE AccountId = :accId
            WITH SECURITY_ENFORCED
            ORDER BY Name
        ];
    }
}

2. Lightning Web Component の作成

次に、LWC のバンドルを作成します。コンポーネント名は `contactList` とします。

contactList.html (HTML テンプレート)

<!-- lightning-card は、Salesforce Lightning Design System に準拠したカードスタイルのコンテナです。 -->
<template>
    <lightning-card title="Related Contacts" icon-name="standard:contact">
        <!-- contacts.data が true (データが存在する) の場合にのみ内部の div を表示します。 -->
        <div class="slds-m-around_medium">
            <template if:true={contacts.data}>
                <!-- template for:each を使用して、取得した contacts のリストをループ処理します。 -->
                <!-- for:item="contact" は、各ループの要素を "contact" という変数に格納します。 -->
                <!-- key={contact.Id} は、リストの各要素を一意に識別するための必須属性です。 -->
                <template for:each={contacts.data} for:item="contact">
                    <p key={contact.Id}>
                        <b>{contact.Name}</b> - {contact.Title}
                    </p>
                </template>
            </template>

            <!-- contacts.error が true (エラーが発生した) の場合にのみエラーメッセージを表示します。 -->
            <template if:true={contacts.error}>
                <p>An error occurred while loading contacts.</p>
            </template>
        </div>
    </lightning-card>
</template>

contactList.js (JavaScript コントローラー)

// LWC の基本モジュールとデコレータを lightning/lwc からインポートします。
import { LightningElement, api, wire } from 'lwc';

// 先ほど作成した Apex メソッドをインポートします。
// 'c' 名前空間は、同じプロジェクト内のコンポーネントや Apex クラスを指します。
import getContacts from '@salesforce/apex/ContactController.getContacts';

export default class ContactList extends LightningElement {
    // @api デコレータを使用して recordId プロパティを公開します。
    // このコンポーネントがレコードページに配置されると、Salesforce が自動的に現在のレコードIDをこのプロパティに渡します。
    @api recordId;

    // @wire デコレータを使用して Apex メソッドを呼び出します。
    // 第1引数: 呼び出す Apex メソッド。
    // 第2引数: メソッドに渡すパラメータのオブジェクト。
    // '$recordId' のように '$' を接頭辞に付けると、recordId プロパティの変更にリアクティブに応答します。
    // recordId の値が変わると、@wire は自動的に Apex メソッドを再実行します。
    // 結果は contacts プロパティに格納されます。このプロパティは { data, error } という形式のオブジェクトになります。
    @wire(getContacts, { accId: '$recordId' })
    contacts;
}

contactList.js-meta.xml (メタデータ設定)

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <!-- apiVersion は、コンポーネントが使用する Salesforce プラットフォームの API バージョンです。 -->
    <apiVersion>58.0</apiVersion>
    <!-- isExposed を true に設定すると、Lightning アプリケーションビルダーなどのツールでこのコンポーネントが表示されるようになります。 -->
    <isExposed>true</isExposed>
    
    <!-- targets タグで、このコンポーネントを配置できる場所を定義します。 -->
    <targets>
        <!-- lightning__RecordPage は、レコード詳細ページを意味します。 -->
        <target>lightning__RecordPage</target>
    </targets>

    <!-- targetConfigs は、特定のターゲット (ここではレコードページ) でのコンポーネントの動作をさらに詳細に設定します。 -->
    <targetConfigs>
        <targetConfig targets="lightning__RecordPage">
            <!-- このコンポーネントが取引先 (Account) オブジェクトのレコードページでのみ利用可能になるように制限します。 -->
            <objects>
                <object>Account</object>
            </objects>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

これらのファイルをデプロイした後、Lightning アプリケーションビルダーで取引先のレコードページを開き、カスタムコンポーネント `contactList` をページにドラッグ&ドロップすれば、設定は完了です。


注意事項

権限と共有設定

Apex クラスには必ず `with sharing` または `without sharing` を明記することが推奨されます。`with sharing` を使用すると、現在のユーザーの共有ルールが適用され、ユーザーがアクセス権を持たないレコードはクエリ結果から除外されます。また、`WITH SECURITY_ENFORCED` 句や `Security.stripInaccessible` メソッドを使用して、項目レベルのセキュリティ (FLS) を徹底することも重要です。

API 制限とガバナ制限

LWC から呼び出される Apex メソッドも、他の Apex トランザクションと同様にガバナ制限の対象となります。SOQL クエリの発行回数 (100回)、クエリで取得する合計行数 (50,000行) などの制限に注意してください。大量のデータを扱う場合は、ページネーションや遅延読み込みの実装を検討する必要があります。

エラー処理

`@wire` サービスは、成功した場合は `data` プロパティに、失敗した場合は `error` プロパティに値を格納します。HTML テンプレート内で `if:true={contacts.error}` のようなディレクティブを使用して、ユーザーフレンドリーなエラーメッセージを表示することが重要です。これにより、データがロードできなかった場合に空白のコンポーネントが表示されるのを防ぎます。


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

Lightning Web Components は、Salesforce Lightning Experience 上でリッチでインタラクティブなユーザー体験を構築するための、現代的で強力なフレームワークです。Web 標準に基づいているため、学習コストが比較的低く、パフォーマンスも非常に高いという利点があります。

LWC を開発する際のベストプラクティスを以下に示します。

  1. コンポーネントの粒度を小さく保つ: 一つのコンポーネントに多くの機能を詰め込むのではなく、特定の責務を持つ小さなコンポーネントを複数作成し、それらを組み合わせることで、再利用性とメンテナンス性が向上します。
  2. Base Lightning Components を最大限に活用する: `lightning-card` や `lightning-datatable`、`lightning-input` といった Salesforce が提供する基本コンポーネントを積極的に使用しましょう。これにより、Lightning Design System に準拠した一貫性のある UI を迅速に構築できます。
  3. Lightning Data Service (LDS) を優先する: `@wire` を使用して LDS や Apex メソッドを呼び出すことで、キャッシュ、データの一貫性、パフォーマンスの恩恵を最大限に受けることができます。DML 操作 (作成、更新、削除) や、キャッシュが不要な処理の場合にのみ、命令的な Apex 呼び出しを検討します。
  4. Apex のセキュリティを確保する: サーバーサイドの Apex コードでは、常に共有ルール (`with sharing`)、項目レベルのセキュリティ、CRUD 権限を考慮して、堅牢なセキュリティを実装してください。
  5. テストを徹底する: Apex クラスには必ず単体テストを作成し、十分なコードカバレッジを確保します。LWC 側も Jest を用いた単体テストを作成することで、コンポーネントの品質を保証できます。

これらの原則に従うことで、あなたは Lightning Experience の可能性を最大限に引き出す、高品質でスケーラブルなカスタムソリューションを構築できるでしょう。

コメント