LWC

親LWCと子LWCへ確認画面と更新処理を分ける実装

1つのLWCに確認画面と更新処理を詰め込むと、あとで差し替えにくくなります。親から子へ属性で値を渡し、子から親へカスタムイベントで結果を返す分け方を説明します。

2023.12.01

なぜ親子に分けるのか

確認画面と更新処理を1つのLWCへ詰め込むと、見た目とロジックが密結合になります。入り口になる親と、実際に処理する子を分けると、確認画面だけ差し替えたいときに親だけを直せます。この記事は、取引先詳細のクイックアクションを例に、親子2つのLWCへ分ける実装を扱います。

全体の構成

コンポーネント役割
No.1(親)クイックアクションの入り口。recordIdと対象項目のAPI参照名を、子へ属性として渡す
No.2(子)確認パネルを表示し、実行ボタンでlightning/uiRecordApiのレコード更新を行う
親のLWC No.1の枠の中に、子のLWC No.2の枠が入れ子で描かれた構成図
親と子の入れ子を図にしたものです。外側の青が入り口になる親のNo.1、内側の緑が確認と更新を担う子のNo.2にあたります。

親から子へは@api属性で値を渡し、子から親へはカスタムイベントで結果を返します。この2つが、親子コンポーネント間でもっとも基本的なやり取りの形です。

親から子へ値を渡す

c-screen-no-2-actionというタグ名で、ハイフン区切りのケバブケースへ変わっている点に注目してください。LWCのファイル名screenNo2Actionは、マークアップの中ではc-screen-no-2-actionになります。属性のrecord-id・id-field-api-nameも同様に、キャメルケースがケバブケースへ変わります。

screenNo1Action.html

<template>
    <c-screen-no-2-action record-id={recordId} id-field-api-name={idFieldApiName}
        onfinish={handleFinish} oncancel={handleCancel}>
        <span slot="description">
            <p>screenNo1Action</p>
        </span>
    </c-screen-no-2-action>
</template>

子コンポーネントへrecordIdとidFieldApiNameを属性で渡し、finish・cancelのカスタムイベントを受け取ります。named slotのdescriptionへ説明文を差し込んでいます。

screenNo1Action.js

import { api, LightningElement } from "lwc";
import { CloseActionScreenEvent } from "lightning/actions";
import ID from "@salesforce/schema/Account.Id";
export default class ScreenNo1Action extends LightningElement {
    @api recordId;
    @api idFieldApiName = ID.fieldApiName;
    handleFinish() {
        this.dispatchEvent(new CloseActionScreenEvent());
    }
    handleCancel() {
        this.dispatchEvent(new CloseActionScreenEvent());
    }
}

recordIdはクイックアクションから自動で受け取る@apiプロパティです。finish・cancelどちらのイベントでも、画面アクションを閉じるだけの役割です。

⚠️ handleFinish・handleCancelはテンプレートのonfinish・oncancelからしか呼ばれないイベントハンドラです。外部から直接呼ぶ入り口ではないため、@apiは付けません。

screenNo1Action.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>67.0</apiVersion>
    <isExposed>true</isExposed>
    <targets>
        <target>lightning__RecordAction</target>
    </targets>
    <targetConfigs>
        <targetConfig targets="lightning__RecordAction">
            <actionType>ScreenAction</actionType>
        </targetConfig>
    </targetConfigs>
</LightningComponentBundle>

apiVersionを58.0から67.0(Summer '26)へ上げています。

子から親へ結果を返す

screenNo2Action.html

<template>
    <lightning-quick-action-panel header="SampleScreenNo2Action">
        <div class="slds-m-top_medium slds-m-bottom_x-large">
            <h2 class="slds-text-heading_medium slds-m-bottom_medium">screenNo2Action</h2>
        </div>
        <slot name="description"></slot>
        <div slot="footer">
            <lightning-button variant="neutral" label="キャンセル" onclick={handleClickCancel} class="slds-m-left_x-small"></lightning-button>
            <lightning-button variant="brand" label="実行" onclick={handleClickExecute} class="slds-m-left_x-small"></lightning-button>
        </div>
    </lightning-quick-action-panel>
</template>

lightning-quick-action-panelで見た目を統一し、named slotで親から受け取った説明文を表示します。

screenNo2Action.js

import { api, LightningElement } from "lwc";
import { updateRecord } from "lightning/uiRecordApi";
import { RefreshEvent } from "lightning/refresh";
export default class ScreenNo2Action extends LightningElement {
    @api recordId;
    @api idFieldApiName;
    async handleClickExecute() {
        const fields = {};
        fields[this.idFieldApiName] = this.recordId;
        await updateRecord({ fields });
        this.dispatchEvent(new RefreshEvent());
        this.dispatchEvent(new CustomEvent("finish"));
    }
    handleClickCancel() {
        this.dispatchEvent(new CustomEvent("cancel"));
    }
}

子のクラス名はScreenNo2Actionです。画面の更新には、lightning/refreshのRefreshEventを使っています。

以前の書き方ではeval("$A.get('e.force:refreshView').fire()")で画面を更新していました。これはLWCの中でAuraのグローバル関数を直接呼ぶ書き方で、公式に案内されている方法ではありません。現在はlightning/refreshモジュールのRefreshEventを使うのが、コンテナへ更新を伝える公式な方法です。handleClickExecute・handleClickCancelも、自分のテンプレートからしか呼ばれないため@apiは付けません。

screenNo2Action.js-meta.xml

<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
    <apiVersion>67.0</apiVersion>
    <isExposed>false</isExposed>
</LightningComponentBundle>

apiVersionを67.0へ上げ、isExposedをfalseにしています。No.2は親であるNo.1から呼ばれる子として使う前提で、単独のクイックアクションとしては公開しません。

アクションとして配置する

  1. 取引先のオブジェクトマネージャを開く

    設定のオブジェクトマネージャから、取引先を選びます。

  2. アクションを新規作成する

    ボタン、リンク、アクションから新規アクションを作り、アクションタイプに「Lightning Web コンポーネント」を選び、対象コンポーネントには親であるNo.1(screenNo1Action)を指定します。

  3. ページレイアウトへ配置する

    取引先のページレイアウト編集画面で、作成したアクションをアクション欄へドラッグして保存します。

アクション「多重LWC」の詳細画面。Lightning Webコンポーネント欄にscreenNo1Actionが設定されている
作成したアクションの詳細です。赤枠のとおり、対象のLightning Webコンポーネントは親のscreenNo1Action、サブ種別は画面アクションになります。

動作を確認する

  1. 取引先詳細画面でアクションを実行する

    配置したアクションを押すと、親のNo.1を通じて子のNo.2が確認パネルとして開きます。

  2. キャンセルを押す

    何もせず、アクション画面だけが閉じます。

  3. 実行を押す

    子のNo.2が対象項目を更新し、更新後にRefreshEventで画面を更新してから、アクション画面が閉じます。

取引先の詳細画面。上部のボタン群に赤枠で囲まれた「多重LWC」ボタンが並んでいる
ページレイアウトへ配置すると、取引先詳細の上部に赤枠のボタンが出ます。ここが親のNo.1を呼び出す入り口です。
開いた確認パネル。見出しはSampleScreenNo2Actionで、下にキャンセルと実行のボタンがある
ボタンを押して開いたパネルです。緑で囲まれたパネル全体が子のNo.2で、青のscreenNo1Actionだけが親からslotで渡された部分です。
取引先の詳細画面に戻った状態。関連リストの取引先履歴は3件と表示されている
キャンセルを押した直後の画面です。パネルが閉じるだけで、関連リストの取引先履歴は3件のまま、レコードは更新されません。
実行後の取引先詳細画面。赤枠で囲まれた取引先履歴が4件に増えている
実行を押したあとの画面です。赤枠の取引先履歴が3件から4件へ増え、レコードが更新されたことが分かります。

親子以外へ渡したいとき

ここまでの@api属性とカスタムイベントは、直接の親子関係にあるコンポーネント間でしか使えません。兄弟コンポーネントや、同じLightningページ内の離れたコンポーネント同士で値をやり取りしたいときは、lightning/messageService(Lightning Message Service)を使います。メッセージチャネルを介したpublish・subscribeの形になり、親子関係を持たないコンポーネント間の通信に向いています。

ここで間違えやすい

間違い何が起きるか
コピペしたクラス名をそのまま直さない動きはしても、デバッグ時に何のクラスか分かりにくくなります
LWCの中でeval("$A.get(...)")のようにAuraのグローバルを呼ぶ公式に案内されている方法ではありません。lightning/refreshのRefreshEventを使います
子コンポーネントも単独のクイックアクションとして公開したままにする使われない入り口が増え、レイアウトの候補が分かりにくくなります
親子関係の無いコンポーネント間で@api属性を使おうとするそもそも属性を渡す先が無く、値が届きません。Lightning Message Serviceを使います

確認した環境

  • 2026年9月 / Salesforce Summer '26(API バージョン 67.0)時点の公式ドキュメントで、カスタムイベントの送受信、lightning/refreshによるコンテナ更新、Lightning Message Serviceの位置づけを確認しています
  • コードは API バージョン 67.0 で書いています

まとめ

  • 確認画面と更新処理は、親と子のLWCへ分けると差し替えやすくなります
  • 親から子へは@api属性、子から親へはカスタムイベントで値をやり取りします
  • eval("$A.get(...)")によるAura経由の画面更新は、lightning/refreshのRefreshEventへ書き換えます
  • 子コンポーネントを単独で公開する必要が無ければ、isExposedはfalseにしておきます
  • 親子関係の無いコンポーネント間の通信には、Lightning Message Serviceを使います
  • apiVersionは58.0から67.0(Summer '26)へ上げています

Salesforceの導入・運用についてご相談ください

導入前の検討から、お使いの環境の改修・運用、AIとの連携まで承ります。状況を伺ったうえで、進め方をご提案します。