Lightning Web Components間で連携する

Lightningアプリケーションビルダーに、Lightning Web Componentsを2つ配置します。

片方の状態が変わったら(例えば、選択した行が切り替わったら)、もう片方の表示を切り替えるということをやってみようとしたら、思ったより苦労しました。(これくらい標準機能でできるものとばかり・・・)

調べて見ますと、pubsub.jsを使うといいということにたどり着きました。

Lightning Web Components間で連携するには、イベントを使います。子コンポーネントから親コンポーネントは、正式なやり方がありますが、コンポーネント間は、自分でやってねってことです。

イベントを管理するソースコードを準備する

自分で管理すると言うことなので、イベントを登録する、イベントを発火させると言った事を担当するソースコードを準備します。インターネット上でみる、pubsub.jsそのものです。

/**
 * A basic pub-sub mechanism for sibling component communication
 *
 * TODO - adopt standard flexipage sibling communication mechanism when it's available.
 */

const events = {};

const samePageRef = (pageRef1, pageRef2) => {
    const obj1 = pageRef1.attributes;
    const obj2 = pageRef2.attributes;
    return Object.keys(obj1)
        .concat(Object.keys(obj2))
        .every(key => {
            return obj1[key] === obj2[key];
        });
};

/**
 * Registers a callback for an event
 * @param {string} eventName - Name of the event to listen for.
 * @param {function} callback - Function to invoke when said event is fired.
 * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
const registerListener = (eventName, callback, thisArg) => {
    // Checking that the listener has a pageRef property. We rely on that property for filtering purpose in fireEvent()
    if (!thisArg.pageRef) {
        throw new Error(
            'pubsub listeners need a "@wire(CurrentPageReference) pageRef" property'
        );
    }

    if (!events[eventName]) {
        events[eventName] = [];
    }
    const duplicate = events[eventName].find(listener => {
        return listener.callback === callback && listener.thisArg === thisArg;
    });
    if (!duplicate) {
        events[eventName].push({ callback, thisArg });
    }
};

/**
 * Unregisters a callback for an event
 * @param {string} eventName - Name of the event to unregister from.
 * @param {function} callback - Function to unregister.
 * @param {object} thisArg - The value to be passed as the this parameter to the callback function is bound.
 */
const unregisterListener = (eventName, callback, thisArg) => {
    if (events[eventName]) {
        events[eventName] = events[eventName].filter(
            listener =>
                listener.callback !== callback || listener.thisArg !== thisArg
        );
    }
};

/**
 * Unregisters all event listeners bound to an object.
 * @param {object} thisArg - All the callbacks bound to this object will be removed.
 */
const unregisterAllListeners = thisArg => {
    Object.keys(events).forEach(eventName => {
        events[eventName] = events[eventName].filter(
            listener => listener.thisArg !== thisArg
        );
    });
};

/**
 * Fires an event to listeners.
 * @param {object} pageRef - Reference of the page that represents the event scope.
 * @param {string} eventName - Name of the event to fire.
 * @param {*} payload - Payload of the event to fire.
 */
const fireEvent = (pageRef, eventName, payload) => {
    if (events[eventName]) {
        const listeners = events[eventName];
        listeners.forEach(listener => {
            if (samePageRef(pageRef, listener.thisArg.pageRef)) {
                try {
                    listener.callback.call(listener.thisArg, payload);
                } catch (error) {
                    // fail silently
                }
            }
        });
    }
};

export {
    registerListener,
    unregisterListener,
    unregisterAllListeners,
    fireEvent
};

イベントを受ける側の実装

イベントを受ける側の実装を行います。

connectedCallbackでイベントを受けるための登録を行います。disconnectedCallbackで登録したイベントを削除します。イベントを受けたときに呼び出すハンドラを実装します。

ここでのポイントは、import { CurrentPageReference } from 'lightning/navigation'; の定義を行う事と、@wire(CurrentPageReference) pageRef; を忘れないことです。

import { LightningElement, track, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
import { registerListener, unregisterAllListeners } from 'c/aclPubSub';

export default class AclReceiveOrder extends LightningElement {
    /**
     * 締めフラグ
     */
    @track isClosing = false;

    /**
     * 現ページ情報
     */
    @wire(CurrentPageReference) pageRef;

    /**
     * 初期化処理
     */
    connectedCallback() {
        // イベントを受けるための登録を行う
        registerListener('changedreceiveorder', this.handleChangedReceiveOrder, this);
    }

    /**
     * 終了処理
     */
    disconnectedCallback() {
        // 登録したイベントを削除する
        unregisterAllListeners(this);
    }

    /**
     * 表示する受注情報の変更
     * @param {*} value 受注情報
     */
    handleChangedReceiveOrder(value) {
        // 締めフラグを設定する
        this.isClosing = value;
    }
}

イベントを発行する側の実装

次にイベントを発行する側を実装します。

pubsub.jsのfireEventを使います。

こちらでも、import { CurrentPageReference } from 'lightning/navigation'; の定義を行う事と、@wire(CurrentPageReference) pageRef; が必要ですので、お忘れなく。

import { LightningElement, track, wire } from 'lwc';
import { CurrentPageReference } from 'lightning/navigation';
import { fireEvent } from 'c/aclPubSub';

export default class AclReceiveOrderList extends LightningElement {

    /**
     * 現ページ情報
     */
    @wire(CurrentPageReference) pageRef;

    /**
     * 受注明細がクリックされたら、そのIDをselectedに設定する
     * @param {*} event 
     */
    handleChangedReceiveOrderClick(event) {
        // changedreceiveOrderイベントを発行する
        fireEvent(this.pageRef, 'changedreceiveorder', event.target.value);        
    }
}