親LWCと子LWCへ確認画面と更新処理を分ける実装
1つのLWCに確認画面と更新処理を詰め込むと、あとで差し替えにくくなります。親から子へ属性で値を渡し、子から親へカスタムイベントで結果を返す分け方を説明します。
なぜ親子に分けるのか
確認画面と更新処理を1つのLWCへ詰め込むと、見た目とロジックが密結合になります。入り口になる親と、実際に処理する子を分けると、確認画面だけ差し替えたいときに親だけを直せます。この記事は、取引先詳細のクイックアクションを例に、親子2つのLWCへ分ける実装を扱います。
全体の構成
| コンポーネント | 役割 |
|---|---|
| No.1(親) | クイックアクションの入り口。recordIdと対象項目のAPI参照名を、子へ属性として渡す |
| No.2(子) | 確認パネルを表示し、実行ボタンでlightning/uiRecordApiのレコード更新を行う |
親から子へは@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から呼ばれる子として使う前提で、単独のクイックアクションとしては公開しません。
アクションとして配置する
取引先のオブジェクトマネージャを開く
設定のオブジェクトマネージャから、取引先を選びます。
アクションを新規作成する
ボタン、リンク、アクションから新規アクションを作り、アクションタイプに「Lightning Web コンポーネント」を選び、対象コンポーネントには親であるNo.1(screenNo1Action)を指定します。
ページレイアウトへ配置する
取引先のページレイアウト編集画面で、作成したアクションをアクション欄へドラッグして保存します。
動作を確認する
取引先詳細画面でアクションを実行する
配置したアクションを押すと、親のNo.1を通じて子のNo.2が確認パネルとして開きます。
キャンセルを押す
何もせず、アクション画面だけが閉じます。
実行を押す
子のNo.2が対象項目を更新し、更新後にRefreshEventで画面を更新してから、アクション画面が閉じます。
親子以外へ渡したいとき
ここまでの@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との連携まで承ります。状況を伺ったうえで、進め方をご提案します。