2023.02.01

Lightning Web Component間の通信方法

はじめに

みなさん、Lightning Web Component(以下、LWC)で開発はしていますでしょうか?
私はテラスカイに入社してから毎日のようにLWCで開発しています。
さっそくですが、今回はLWC間の通信方法について紹介してみようと思います。
この記事でLWC間の通信方法の参考になれば幸いです。

Lightning Web Component間の通信方法について

LWC間の主な3種類の通信方法についてご紹介します。

 1.publicプロパティ、publicメソッド
 2.カスタムイベント
 3.Lightning Message Service

publicプロパティ、publicメソッド

publicプロパティ、publicメソッドは親コンポーネントから子コンポーネントへ値を渡す方法になります。
親コンポーネントから@apiデコレーターを使用して、子コンポーネントへ値を直接渡します。

下記の例では、親コンポーネントで選択された値を、publicプロパティもしくは、publicメソッドを使用して値を連携し、子コンポーネント側で画面表示をします。

publicプロパティ

親コンポーネント
<template>
        <lightning-card title="Parent">
                <div class="slds-grid">
                        <div class="slds-p-around_medium">
                                <lightning-combobox 
                                        name="value" 
                                        label="value" 
                                        value={value} 
                                        options={options} 
                                        onchange={handleChange} 
                                        variant="label-hidden">
                                </lightning-combobox>
                        </div>
                        <div class="slds-p-around_medium">
                                <c-public-child value={value}></c-public-child>
                        </div>
                </div>
        </lightning-card>
</template>
publicParent.html
import { LightningElement } from 'lwc';

export default class PublicParent extends LightningElement {
        value = '';

        get options() {
                return [
                        { label: '', value: '' },
                        { label: 'A', value: 'A' },
                        { label: 'B', value: 'B' },
                        { label: 'C', value: 'C' }
                ];
        }

        handleChange(event) {
                this.value = event.detail.value;
        }
}
publicParent.js
子コンポーネント
<template>
        <!-- 親から渡された値を表示する -->
        <div>
                <span>通信値 {value}</span>
        </div>
</template>
pulicChild.html
import { LightningElement, api } from 'lwc';

export default class PublicChild extends LightningElement {
        // publicプロパティは@apiデコレーターを使用して宣言します。
        // @apiをつけることで親コンポーネントからアクセス可能になります。
        @api value;
}
publicChild.js

publicメソッド

publicメソッドを使用してpublicプロパティと同様な実装すると下記のようになります。
querySelectorメソッドで子コンポーネントを取得し、メソッドを呼び出します。
親コンポーネント
<template>
        <lightning-card title="Parent">
                <div class="slds-grid">
                        <div class="slds-p-around_medium">
                                <lightning-combobox 
                                        name="value" 
                                        label="value" 
                                        value={value} 
                                        options={options} 
                                        onchange={handleChange} 
                                        variant="label-hidden">
                                </lightning-combobox>
                        </div>
                        <div class="slds-p-around_medium">
                                <c-public-child></c-public-child>
                        </div>
                </div>
        </lightning-card>
</template>
publicParent.html
import { LightningElement } from 'lwc';

export default class PublicParent extends LightningElement {
        value = '';

        get options() {
                return [
                        { label: '', value: '' },
                        { label: 'A', value: 'A' },
                        { label: 'B', value: 'B' },
                        { label: 'C', value: 'C' }
                ];
        }

        handleChange(event) {
                this.value = event.detail.value;
                // querySelectorメソッドで子コンポーネントを取得し、publicメソッドを呼び出します。
                this.template.querySelector('c-public-method-child').selectValue(this.value);
        }
}
publicParent.js
子コンポーネント
import { LightningElement, api } from 'lwc';

export default class PublicMethodChild extends LightningElement {
        // publicメソッドは@apiデコレーターを使用して宣言します。
        // @apiをつけることで親コンポーネントからアクセス可能になります。
        value;
        @api
        selectValue(value) {
                this.value = value;
        }
}
publicChild.js

カスタムイベント

カスタムイベントは子コンポーネントから親コンポーネントに値を渡す方法になります。
子コンポーネントでカスタムイベントをディスパッチし、親コンポーネントのイベントハンドラーで処理を実行します。

下記の例では、子コンポーネントをクリックした際に、カスタムイベントをディスパッチして、親コンポーネントにイベント名の値を連携し、親コンポーネント側で画面表示をします。
親コンポーネント
<template>
        <lightning-card title="Parent">
                <p class="slds-align_absolute-center slds-text-heading_large">
                        <span>選択した子コンポーネントイベント: {selectValue}</span>
                </p>
                <div class="slds-m-top_xx-large slds-align_absolute-center">
                        <!-- on「カスタムイベント名」で、カスタムイベントのリスナーを宣言します。
                 ここでは「handleSelect」がリスナーとして宣言されています。 -->
                        <c-custom-child value="イベント A" onselect={handleSelect} class="slds-m-horizontal_small"> </c-custom-child>
                        <c-custom-child value="イベント B" onselect={handleSelect} class="slds-m-horizontal_small"> </c-custom-child>
                        <c-custom-child value="イベント C" onselect={handleSelect} class="slds-m-horizontal_small"> </c-custom-child>
                </div>
        </lightning-card>
</template>
customParent.html
import { LightningElement } from 'lwc';

export default class CustomParent extends LightningElement {
        selectValue = '';

        // カスタムイベント発火時にhandleSelectメソッドで処理します。
        // 子コンポーネントの値は「event.detail.value」で取り出すことができます。
        handleSelect(event) {
                this.selectValue = event.detail.value;
        }
}
customParent.js
子コンポーネント
<template>
        <!-- 子コンポーネントをクリックしたときにhandleClickメソッドを実行します。 -->
        <a onclick={handleClick}>
                <div class="sample">
                        <span>{value}</span>
                </div>
        </a>
</template>
customChild.html
import { LightningElement, api } from 'lwc';

export default class CustomChild extends LightningElement {
        @api value = '';

        // dispatchEventメソッドでカスタムイベントをディスパッチし、親コンポーネントにイベントを送信する。
        handleClick() {
                const selectEvent = new CustomEvent('select', {
                        detail: {
                                value: this.value
                        }
                });
                this.dispatchEvent(selectEvent);
        }
}
customChild.js

LWCでイベント通信を行うとき、通常は1つ上のコンポーネントまでしかイベント通信ができません。
しかし、2つ上の階層(またはそれ以上)にあるコンポーネントにはイベント作成時にbubbles: true, composed: trueとすることでイベント通信することが可能になります。
import { LightningElement, api } from 'lwc';

export default class CustomChild extends LightningElement {
        @api value = '';

        // dispatchEventメソッドでカスタムイベントをディスパッチし、2つ上の階層(またはそれ以上)のコンポーネントにイベントを送信可能とする。
        handleClick() {
                const selectEvent = new CustomEvent('select', {
                        detail: {
                                value: this.value
                        },
                        bubbles: true,
                        composed: true
                });
                this.dispatchEvent(selectEvent);
        }
}
customChild.js

Lightning Message Service

階層関係(親子関係)にないコンポーネントへの通信にはLightning Message Service(以下、LMS)を使用します。LMSはLWCだけではなく、VisualforceやAuraでも使用することができます。
LMSを使用するには以下が必要になります。

 1.MessageChannel(メタデータ)の作成
 2.受取側のコンポーネントでメッセージチャネルをインポートして、購読(Subscribe)する
 3.送信側のコンポーネントでメッセージチャネルをインポートして、送信したいデータを公開(Publish)する

※公開(Publish)を実行すると、購読(Subscribe)したすべてのコンポーネントにデータが送信されます。

下記の例では、Publishコンポーネントで選択した値をLMSで公開(Publish)します。SubscribeコンポーネントはLMSを購読(Subscribe)し、Publishコンポーネントで公開(Publish)された値を画面表示します。

MessageChannel

<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
    <isExposed>true</isExposed>
    <lightningMessageFields>
        <fieldName>value</fieldName>
    </lightningMessageFields>
    <masterLabel>This is a sample Lightning Message Channel.</masterLabel>
</LightningMessageChannel>
sampleMessage

Subscribeコンポーネント

<template>
        <lightning-card title="Subscriber">
                <!-- 親から渡された値を表示する -->
                <div class="slds-align_absolute-center">
                        <span>通信値 {value}</span>
                </div>
        </lightning-card>
</template>
subscriber.html
import { LightningElement, wire, api } from 'lwc';

// LMSで使用するMessageChannelをインポートします。
import { subscribe, unsubscribe, MessageContext, APPLICATION_SCOPE } from 'lightning/messageService';
import SM from '@salesforce/messageChannel/SampleMessage__c';

export default class Subscriber extends LightningElement {
        @api value;

        // subscribe()の戻り値を格納する変数です。
        subscription;

        // ワイヤーサービスを使用してMessageContextオブジェクトを作成します。
        @wire(MessageContext)
        messageContext;

        // subscribe()を実行し、SampleMessageを購読します。
        connectedCallback() {
                this.subscribeToMessageChannel();
        }

        // unsubscribeメソッドで購読状態を解除できます。
        disconnectedCallback() {
                unsubscribe(this.subscription);
                this.subscription = null;
        }

        subscribeToMessageChannel() {
                this.subscription = subscribe(
                        this.messageContext,
                        SM,
                        // メッセージを受信したときのコールバック関数です。
                        (message) => {
                                this.value = message.value;
                        },
                        // 非必須の引数です。APPLICATION_SCOPEにすることで、LWCが非アクティブでもメッセージを受信できます。
                        { scope: APPLICATION_SCOPE }
                );
        }
}
subscriber.js

Publishコンポーネント

<template>
        <lightning-card title="Publisher">
                <div class="slds-grid">
                        <div class="slds-p-around_medium">
                                <lightning-combobox 
                                        name="value" 
                                        label="value" 
                                        value={value} 
                                        options={options} 
                                        onchange={handleChange} 
                                        variant="label-hidden">
                                </lightning-combobox>
                        </div>
                </div>
        </lightning-card>
</template>
publisher.html
import { LightningElement, wire } from 'lwc';

// LMSで使用するMessageChannelをインポートします。
import { publish, MessageContext } from 'lightning/messageService';
import SMC from '@salesforce/messageChannel/SampleMessage__c';

export default class Publisher extends LightningElement {
        value = '';
        // ワイヤーサービスを使用してMessageContextオブジェクトを作成します。
        @wire(MessageContext)
        messageContext;

        get options() {
                return [
                        { label: '', value: '' },
                        { label: 'A', value: 'A' },
                        { label: 'B', value: 'B' },
                        { label: 'C', value: 'C' }
                ];
        }

        handleChange(event) {
                this.value = event.detail.value;
                this.sendMessage(this.value);
        }

        sendMessage(value) {
                // publishメソッドで選択リストの値をSampleMessageに公開します。
                const payload = {
                        type: 'LightningMessageService',
                        value: this.value
                };
                publish(this.messageContext, SMC, payload);
        }
}
publisher.js

最後に

今回は、LWC間の通信方法について紹介しました。
簡単にですが、各通信方法が確認できるサンプルを作成しましたので、みなさんも実装して動きを確認してみてください。

最後までお読みいただき、ありがとうございました。
46 件
     
  • banner
  • banner

関連する記事