【必読】なぜ今、Aura ではなく LWR を選ぶべきなのか?

LWC設定画面をカスタマイズ!Custom Property Editor (CPE) 実装ガイド【基礎からApex連携まで】


LWCでExperience Cloud用コンポーネントを作成する際、js-meta.xml でプロパティを定義すると、Experience Builder(設定画面)上に自動的に入力欄が表示されます。

しかし、標準のテキストボックスやチェックボックスだけでは、「直感的ではない」「設定ミスが起きやすい」と感じることはありませんか?

今回は、架空の紅茶ECサイト「The Royal Brew」のキャンペーンバナーを作成しながら、この設定画面自体をLWCで作る技術、Custom Property Editor (CPE) について解説します。

  • 基礎編: バナーの画像の「不透明度」をスライダーで直感的に調整する。
  • 応用編: バナーの「リンク先カテゴリ」をSalesforceの商品データから動的に選択する。

この2段階で、アドミンにとって優しい設定画面(UX for Admins)を構築していきましょう。

DXforce Point for Developers

今回の記事で作成したコンポーネントの全ソースコードをGitHubで公開しています。 ぜひ Clone して、あなたの組織で動かしてみてください。

シナリオ: 「キャンペーンバナー」の要件

作成するのは、トップページに配置する c-campaign-banner です。 運用担当者からは、以下の要望が出ています。

  1. 「文字が読みやすいように、画像の明るさ(不透明度)を微調整したい。でも数値入力は面倒。」
  2. 「クリックした時の遷移先カテゴリを選びたい。手入力だとIDを間違えるので、リストから選びたい。」

これらをCPEで解決します。

【基礎編】スライダーで直感的な数値入力を実装する

まずは要望1の解決です。標準の数値入力欄を、0%〜100%のスライダーに置き換えます。

エディタ用コンポーネントの作成 (c-opacity-slider-editor)

設定画面のUIとなるコンポーネントです。 CPEとして動作するためには、@api value で値を受け取り、変更時に valuechange イベントを発火する必要があります。

HTML (opacitySliderEditor.html)

<template>
    <div class="slds-p-around_x-small">
        <lightning-slider
            label={label}
            value={value}
            min="0"
            max="100"
            step="10"
            onchange={handleChange}>
        </lightning-slider>
        
        <div class="slds-text-body_small slds-text-color_weak slds-m-top_xx-small">
            現在の不透明度: {value}%
        </div>
    </div>
</template>

JavaScript (opacitySliderEditor.js)

import { LightningElement, api } from 'lwc';

export default class OpacitySliderEditor extends LightningElement {
    // js-meta.xml で定義されたプロパティの現在値が自動的に渡されます
    @api value;

    // js-meta.xml で定義されたラベルが渡されます
    @api label;

    /**
     * スライダーの値が変更された時の処理
     * @param {Event} event 
     */
    handleChange(event) {
        const newValue = event.detail.value;

        // 値の変更をExperience Builderに通知するカスタムイベントを発火
        // イベント名は必ず 'valuechange' である必要があります
        this.dispatchEvent(new CustomEvent('valuechange', {
            detail: {
                value: newValue
            }
        }));
    }
}

XML (opacitySliderEditor.js-meta.xml)

エディタ自体は画面に配置しないため、isExposedfalse です。

これで、「不透明度」の設定用パーツが完成しました。

【応用編】Apex連携で動的な選択リストを作成する

次に要望2の解決です。 標準の設定画面では、選択肢(プルダウン)を作るには js-meta.xml に固定値を書く必要があります。しかし、商品カテゴリが増減するたびにコードを修正するのは非効率です。

そこで、Salesforce組織に登録されている商品カテゴリをApexで取得し、CPE上の選択肢として表示させます。

Apexコントローラー (CpeDynamicController)

Product2オブジェクトの Family 項目からピックリスト値を取得します。

/**
 * @description LWRサイトのCustom Property Editor (CPE) 用のコントローラークラス
 * Experience Builderの設定画面に動的なデータを提供します。
 */
public with sharing class CpeDynamicController {

    /**
     * @description Product2オブジェクトのFamily(商品ファミリ)ピックリスト値を取得します。
     * LWCのcomboboxで使用できる形式(label, value)で返却します。
     * @return List<Map<String, String>> ラベルとAPI参照名を含むマップのリスト
     */
    @AuraEnabled(cacheable=true)
    public static List<Map<String, String>> getProductFamilies() {
        List<Map<String, String>> options = new List<Map<String, String>>();
        
        try {
            // Product2オブジェクトのFamily項目の定義を取得
            Schema.DescribeFieldResult fieldResult = Product2.Family.getDescribe();
            List<Schema.PicklistEntry> ple = fieldResult.getPicklistValues();
            
            // ピックリスト値をループしてリストに追加
            for(Schema.PicklistEntry entry : ple) {
                if(entry.isActive()) {
                    Map<String, String> option = new Map<String, String>();
                    option.put('label', entry.getLabel());
                    option.put('value', entry.getValue());
                    options.add(option);
                }
            }
        } catch (Exception e) {
            System.debug('Error fetching product families: ' + e.getMessage());
        }

        return options;
    }
}

エディタ用コンポーネントの作成 (c-product-family-selector)

Apexから取得した値を lightning-combobox に渡します。

HTML (productFamilySelector.html)

<template>
    <div class="slds-p-around_x-small">
        <template if:true={options}>
            <lightning-combobox
                name="familySelector"
                label={label}
                value={value}
                placeholder="カテゴリを選択してください"
                options={options}
                onchange={handleChange}>
            </lightning-combobox>
        </template>
        
        <template if:true={error}>
            <div class="slds-text-color_error slds-m-top_x-small">
                データの取得に失敗しました。
            </div>
        </template>
    </div>
</template>

JavaScript (productFamilySelector.js)

import { LightningElement, api, wire } from 'lwc';
import getProductFamilies from '@salesforce/apex/CpeDynamicController.getProductFamilies';

export default class ProductFamilySelector extends LightningElement {
    @api value;
    @api label;
    options;
    error;

    // Apexからカテゴリ一覧を取得
    @wire(getProductFamilies)
    wiredProductFamilies({ error, data }) {
        if (data) {
            this.options = data;
            this.error = undefined;
        } else if (error) {
            this.error = error;
        }
    }

    handleChange(event) {
        // 選択されたカテゴリ名をBuilderに通知
        this.dispatchEvent(new CustomEvent('valuechange', {
            detail: { value: event.detail.value }
        }));
    }
}

XML (productFamilySelector.js-meta.xml)

エディタ自体は画面に配置しないため、isExposedfalse です。

これで、「商品カテゴリ選択」の設定用パーツが完成しました。

【完成】メインコンポーネントの実装

最後に、作成した2つのエディタコンポーネントを、メインの c-campaign-banner に適用し、実際の表示ロジックを作成します。

これにより、Experience Builderで設定した「不透明度」と「カテゴリ」が、実際にサイト上のバナーに反映されるようになります。

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

ここで editor 属性を使用し、標準の入力欄を先ほど作成したCPE(カスタムエディタ)に置き換えます。

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>65.0</apiVersion>
    <isExposed>true</isExposed>
    <masterLabel>The Royal Brew キャンペーンバナー</masterLabel>
    
    <targets>
        <target>lightningCommunity__Page</target>
        <target>lightningCommunity__Default</target>
    </targets>

    <targetConfigs>
        <targetConfig targets="lightningCommunity__Default">
            
            <property 
                name="overlayOpacity" 
                type="Integer" 
                label="オーバーレイ不透明度" 
                default="50"
                editor="c/opacitySliderEditor" 
            />

            <property 
                name="targetCategory" 
                type="String" 
                label="リンク先カテゴリ" 
                editor="c/productFamilySelector" 
            />

        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

UIの実装 (campaignBanner.html)

設定値を受け取り、スタイルとリンクを動的に生成します。

<template>
    <div class="banner-container slds-is-relative slds-box slds-box_x-small slds-m-bottom_medium">
        
        <img src="https://images.unsplash.com/photo-1594631252845-29fc4cc8cde9?q=80&amp;w=1000&amp;auto=format&amp;fit=crop" 
             class="banner-image slds-is-absolute" 
             alt="Campaign Background"
             style="width: 100%; height: 100%; object-fit: cover;"
        >

        <div class="overlay slds-is-absolute" style={overlayStyle}></div>

        <div class="content slds-is-relative slds-p-around_large slds-text-align_center">
            <h2 class="slds-text-heading_large slds-m-bottom_medium slds-text-color_inverse">
                Seasonal Selection
            </h2>
            <p class="slds-text-body_regular slds-m-bottom_medium slds-text-color_inverse">
                厳選された茶葉で、最高のティータイムを。
            </p>
            
            <lightning-button 
                variant="brand" 
                label="商品を見る" 
                onclick={handleNavigate}
                class="slds-m-top_small">
            </lightning-button>
        </div>
    </div>
</template>

ロジックの実装 (campaignBanner.js)

CPEから渡された値は、通常の @api プロパティとして受け取ります。

import { LightningElement, api } from 'lwc';
import { NavigationMixin } from 'lightning/navigation';

/**
 * CPE (Custom Property Editor) で設定された値を利用するバナーコンポーネント
 */
export default class CampaignBanner extends NavigationMixin(LightningElement) {
    /**
     * オーバーレイの不透明度 (0-100)
     * c/opacitySliderEditor によって設定されます
     */
    @api overlayOpacity = 50;

    /**
     * 遷移対象の商品カテゴリ (Product Family)
     * c/productFamilySelector によって設定されます
     */
    @api targetCategory;

    /**
     * 不透明度に基づいて背景色のスタイル文字列を生成するGetter
     * スライダーの値 (0-100) を alpha値 (0.0-1.0) に変換します
     * @return {String} style string
     */
    get overlayStyle() {
        // 不透明度をCSS変数として渡す
        const alpha = this.overlayOpacity / 100;
        return `--overlay-alpha: ${alpha}`;
    }

    /**
     * ボタンクリック時の遷移処理
     * 設定されたカテゴリに基づいて検索ページやリストページへ遷移します
     */
    handleNavigate() {
        if (!this.targetCategory) {
            // カテゴリが未設定の場合のハンドリング
            console.warn('Category is not set.');
            return;
        }

        // NavigationMixinを使用してページ遷移(実装例)
        // ※実際の実装では、プロジェクトのサイト構造に合わせたPageReferenceを指定してください
        this[NavigationMixin.Navigate]({
            type: 'standard__search',
            attributes: {
                term: this.targetCategory
            }
        });
        
        // 補足: B2B CommerceやLWRの製品リストページへ遷移する場合の例
        // type: 'standard__webPage',
        // attributes: { url: `/category/${this.targetCategory}` }
    }
}

これで、アドミンはコードを触ることなく、「スライダーでの明度調整」と「組織データに基づくカテゴリ選択」が可能になりました。

さらに広がるCPEの可能性

今回は基本的なスライダーとリスト選択を紹介しましたが、CPEは「エディタ自体がLWC」であるため、JavaScriptで実装できることはほぼ何でも実現可能です。

例えば、以下のような高度な設定画面も作ることができます。

  • JSONジェネレーター(リスト作成):
    • 標準設定では難しい「Q&Aリスト」や「お客様の声」など、複数の項目(質問・回答・名前など)を持つリストデータを、画面上で追加・削除・並び替えしてJSON形式で保存します。
  • ビジュアルアイコンピッカー:
    • SLDSアイコン名をテキスト入力するのではなく、実際のアイコン一覧を表示して、クリックで直感的に選択できるUIを提供します。
  • 外部API連携:
    • Google Maps APIなどをエディタ内に表示し、地図をクリックして緯度経度を取得するなど、外部サービスと連携した設定補助が可能です。
  • 条件付き表示制御:
    • 「レイアウトA」を選んだ時だけ「画像URL」の入力欄を表示するなど、設定項目の依存関係をロジックで制御し、不要な項目を隠してスッキリさせることができます。

CPEを活用して、サイト運用者が「使いやすい!」「楽しい!」と感じる、プロフェッショナルな管理画面(UI)を提供しましょう。

参考URL

Lightning Web Components Dev Guide: Create a Custom Property Editor (※Experience Cloudでの利用も同様の概念に基づきます)

DXforceの管理人

福島 瑛二

2013年にJavaエンジニアとしてのキャリアをスタート。2019年にSalesforceと出会い、Salesforceエンジニアの道へ。

デザインや UI/UX の観点からもシステムを捉え、ユーザーにとって心地よい体験を実装することにやりがいを感じています。

CRM(顧客データ)や Data Cloud と連携した高度なサイトを目に見える形で表現できる Experience Cloud に大きな可能性を見出しており、バックエンドのデータ構造とフロントエンドの表現力を極めることがこれからの Salesforce エンジニアに求められるスキルだと確信しています。

Trailblazer: efukushima

福島 瑛二をフォローする

読者の声

タイトルとURLをコピーしました