0. 対象読者
- SalesforceでLWCをこれから使い始めようとしている人
- LWCでデコレータを使用したいが使用方法がわからない人
- JavaScriptの知識や開発経験がある程度ある人
1. 投稿の動機
LWCの実装当時、デコレータ(@api、@track、@wire)の使い方がわからず、思うよう
に開発が進まないことがありました。
LWC(Lightning Web Component)とはhtml、css、jsを使用してSalesforce上で使用で
きるアプリケーションを開発するための手法です。
デコレータを活用することで、親子関係にあるコンポーネント間でのイベント伝達、値
の受け渡し、リレンダー、Apex処理の呼び出しが可能になります。これらの実装方法を
共有したいと思い、本記事を投稿したいと思いました。
2. デコレータの解説
そもそも「LWCで使用できるデコレータってなんなん?」って思いますよね?
デコレータは変数を定義する時に、変数の前に記述することで使用することができま
す。各デコレータについての説明は以下になります。
・@api:
外部に変数や関数を公開することができる。
具体的にいうと、親コンポーネント側で子コンポーネント変数の値を設定したり、
親コンポーネントのコントローラーで子コンポーネントの関数を呼び出すことが可
能になります。
・@track:
データの格納が可能で、画面に表示可能。
@trackデコレータ月変数は、値が更新さるたびに画面が再描画されます。
そのため、プリミティブ型や配列型、オブジェクト型のいずれの値であっても、
画面上に反映することが可能です。
・@wire:
レコードへのアクセスやApexクラスの処理を呼び出す時に使用する。
(※@wireを使用しなくてもApexクラスのメソッドは呼び出し可能)
3. デコレータ付き変数の定義の記述方法と画面への出力方法
使用するデコレートは'lwc'からインポートする必要があります。
デコレータ付き変数は、クラスをスコープとし、変数の前にデコレータを付与して、メ
ンバー変数として定義します。
構文は以下になります。
※ 以下のファイルを例にコードを記述します。
・LWCバンドル:SampleCmp
import { LightningElement, api, track, wire } from 'lwc';
import {getRecord} from 'lightning/uiRecordApi';
export default class SampleCmp extends LightningElement {
@api val_api; // @apiデコレータ付き変数
@track val_track; // @trackデコレータ付き変数
// @wireデコレータ付きApex処理呼び出し
@wire(getRecord, {
recordId: '0035h000000DN1ciAAD',
fields: ['Contact.Name', 'Contact.Phone']})
contactRecord;
// any code...
}
※ getRecord については公式サイトを参照してください。
定義した変数をhtml上で表示したり、htmlタグのプロパティにセットする記述方法は、
上の構文で記述すると「{val_api}」、「{val_track}」、「{val_nonDecorat}」のように{}
で囲うことで実現することができます。
ボタンをクリックした時、またはリンクをクリックした時に何かしらアクションを起こ
します。このように"onclick"で関数をhtml上から呼び出すことがあります。この場合も
変数と同じように{}で囲って記述することで関数の実行を実現することができます。
ここから各デコレータについてそれぞれ解説していきます。
以降の説明で使用するプロジェクトを以下の構成で作成します。
ディレクトリ構成
Sample/
└ force-app / main / default
├ classes
| └ SampleController.cls
└ lwc
├ childCmp
| ├ childCmp.css
| ├ childCmp.html
| └ parentCmp.js
└ parentCmp
├ parentCmp.css
├ parentCmp.html
└ parentCmp.js
4. @apiデコレータ
@apiデコレータが付与された変数は公開された状態になります。
使用例1:変数の公開
親コンポーネント内で子コンポーネントを呼び出した時、子コンポーネントで定義され
ている@apiを付与した変数に親コンポネントから値を設定することができます。
親コンポーネントから子コンポーネントで公開された変数に値を設定する方法を例を使
って紹介します。
子コンポーネントを以下のように設定します。
childCmp.html
childCmp.js
親コンポーネントで子コンポーネントを呼び出す時に、childCmp.jsで定義した変数をプ
ロパティにセットし、そこに設定したい値をセットする。
parentCmp.html
これをSalesCloudに親コンポーネントを配置してみると以下のようになります。
@apiデコレータ特有の使用方法
レコード詳細画面にLWCのコンポーネントを配置する場合にのみ使用可能です。
"@api recordId"と定義することで変数"recordId"には現在参照しているレコードのIdが
自動的にセットされます。
さらに、"@api objectApiName"と定義することで変数"objectApiName"には現在参照し
ているレコードのオブジェクトAPI参照名が自動的にセットされます。
使用例2:関数の公開
変数だけでなく関数を公開することもできます。つまり、親コンポーネントから子コン
ポーネントの関数を呼び出し実行することが可能ということです。
公開された関数の呼び出し方法について例を紹介します。
子コンポーネントで公開された関数を定義します。
この関数では子コンポーネントが持つ@track変数"clicked"の値を書き換える処理が記述
されています。
"@track clicked"には初期値として"..."を設定しており、関数呼び出し後は"now !"に変更
される処理になっています。
childCmp.html
childCmp.js
親コンポーネントにはボタンを配置し、ボタンがクリックされた時に実行される関数
(handleClick)内で子コンポーネントの公開された関数を呼び出す処理を記述します。parentCmp.html
parentCmp.js
クリックされる前の画面は以下です。
ボタンをクリックすると次のような画面に変更されます。
画面を見てみると、ボタンをクリックする前は"Parent component button is clicked ..."
でしたが、クリック後"Parent component button is clicked now !"に変わっています
ね。
ここで使用されている
"this.template.querySelector.('子コンポーネントタグ').子コンポーネント関数名"
ですが、まずどの子コンポーネントかを
"this.template.querySelector.('子コンポーネントタグ')"
で特定し、その直後にドット繋ぎで子コンポーネントの関数名を記述することで親から
子の関数を呼び出すことができます。
5. @trackデコレータ
使用例1:@track変数による画面の再描画
@trackが付与された変数は値が書き変わる(更新)されるたびに画面が再描画されま
す。@track付きでない変数とは違い、変数がコレクション型でその要素が書き変わった
時にその変更が画面上に反映されます。
初めに、@trackが付与されていない変数で例を見てみましょう。
初期値に"1, 2, 3"を格納するリストがセットされた変数を定義し、ボタンをクリック後
そのリストに"4, 5, 6, 7, 8, 9, 10"を追加する関数を定義します。
parentCmp.html
parentCmp.js
※変数の中身を見るためにconsole.logを配置しました。
ボタンをクリックする前の画面が以下です。
ボタンをクリックしてみると、
となります。画面上では変数の値が変更されたかどうかわかりませんね。
コンソールを見てみましょう。
リストの要素の変更はあったようです。ですが、再描画されていない為、画面上には反
映されていません。
次は@trackを付与して変数を定義してみます。
parentCmp.jsを次のように変更します。
parentCmp.js
@track付与前と同じようにボタンクリック後の動きを見てみましょう。
ボタンクリック前の画面は以下です。
ボタンをクリックすると、
変更内容が画面上でもわかります。コンソールで見てみると、
値のリストの要素の変更も確認できます。
このように@trackを付与した変数の値が更新されると画面が再描画されることがわかり
ます。
6. @wireデコレータ
使用例1:@wireによるレコードへのアクセス
@wireを使用することでレコードの取得やApexクラスのメソッドを呼び出すことができ
ます。まずは"lightning/uiRecordApi"を使用したレコードの取得方法を見てみましょ
う。
① Apexクラスを使用しない@wireによるレコードアクセス
次のサンプルのように記述することでApexクラスを介さずレコードの取得が可能になり
ます。
parentCmp.html
parentCmp.js
画面を表示すると以下のように
取引先責任者に設定されている"名前"と参照先取引先の"取引先名"を表示することがで
きます。
一気に難しくなった感がありますね。。。
parentCmp.htmlの{}で囲われて記述されているところに取得した取引先責任者の名前と
参照先の取引先の取引先名を渡しています。
表示用の取引先責任者レコードの取得をしているのがparentCmp.jsの11~15行になりま
す。取得するレコードの条件をIdで指定しています(12行目)。取得するレコードの項
目を取引先責任者の名前と参照先取引先の取引先名に制限しています(13行目)。
取得した取引先責任者情報は15行目にある"contactRecord"に格納されます。
サンプルコードでは"@api redordId"を使用しているので、開いた取引先責任者のレコー
ド詳細ごとに対応しています。
このように、Salesforceで用意されている"getRecord"を使用することでオブジェクトの
レコードにアクセスすることができます。
注意:"lighting/uiRecordApi"の"getRecord"では取得するレコードのIdを指定する必要
があり、かつIdを条件にすることしかできないことに注意。
※画面に値を返す方法としてgetメソッドを使用していますが、本記事では説明しまん。
② Apexクラスを使用した@wireによるレコードアクセス
次に@wireを使ってApexクラスを呼び出す方法について解説します。
以下のようにApexクラスにIdを条件に取引先責任者レコードを取得するメソッドを記述
します。ここでは取引先責任者の名前と参照先の取引先の取引先名のみを取得すること
にします。
SampleController.cls
LWCでApexクラスメソッドを呼び出すにはApexクラスメソッドに"@AuraEnabled"を付
与する必要があることに注意します。
それではApexクラスにあるメソッドを呼び出してみましょう。
parentCmp.html
parentCmp.js
画面を表示してみるとPattern 1と同じような画面になりました。
このように、@wireを定義して第一引数にApexクラスにあるメソッド名(11行目)、第
二引数に呼び出したApexクラスメソッドの引数(11行目)を設定することでApexクラス
のメソッドにアクセスすることができます。
Apexクラスを使用した@wireの場合、"lightning/uiRecordApi"の"getRecord"とは違って
レコードの取得条件を自由に設定することができます。
使用例2:@wireを使用しないレコードへのアクセス
使用例1では@wireを使用していましたが使用例2では@wireを使用せずにApexクラス
のメソッドにアクセスする方法を紹介します。
呼び出し方法は単純で、jsの関数ないでApexメソッドを記述するだけです。
まずはApexクラスにメソッドを用意します。
SampleController.cls
LWC側で使用するメソッドなので"@AuraEnabled"を付与することを忘れずに!
@wireを使用した時と同じように画面には取引先責任者の名前と参照先取引先の取引先
名を表示するコンポーネントを例とします。
parentCmp.html
parentCmp.js
画面を表示すると下の画像のように取引先責任者の名前と参照先取引先の取引先名が表
示されます。
Apexクラスメソッドを呼び出して引数を設定しています(13行目)。
Apexクラスメソッドから返ってきた値は"@track contact"に格納します(15行目)。
このようにしてApexクラスのメソッドを@wireを使用せずに呼び出すことができます。
〜 補足 〜
ApexクラスのメソッドをLWCで実行すると非同期処理になります。
@wireが付与されたApexクラスメソッドの呼び出しも同じです。
ですが、@wireが付与されたApexクラスメソッドの呼び出しとは違い、使用例2のよう
に、jsの関数内で呼び出している場合はでPromiseやasync/awaitなどを使用して同期処
理にすることが可能です。Promise型として使用することもできるところが@wireを使用
しない場合のメリットです。
7. まとめ
このようにデコレータを使用することで親子関係にあるコンポーネント間での値のやり
とり、画面の再描画によるリストやオブジェクト型の変数の出力、レコードの取得など
を有効に使用することができるようになります。