皆さん、こんにちは。アピリオ北嵐です。LWC入門の3回目となる今回は簡単なサンプル・コンポーネントを通して、LWCの基本的な実装方法とSalesforceサーバーからのデータ取得方法を見ていくことにします。LWCでは新たにWire Serviceというノンコーディングでサーバーからデータを取得できる便利な方法が導入されており、いくつかのパターンでサーバーからデータを取得できます。本稿ではその特長と使い分けについて解説してみます。LWC導入の背景や基本的な構成については、LWC入門(1)および(2)を参照してみてください。
開発環境のセットアップ
LWCはSpring'19時点では開発者コンソール等のSalesforceサーバー環境で作成・開発することができません。開発を行うには、ローカル環境でコーディングを行い、Salesforce CLIを使って新規作成・デプロイを行う必要があります。エディタとしては VisualStudio Code が推奨エディタとなっています。Salesforce CLIは開発およびビルドの自動化を行うためのコマンドライン・インターフェースであり、従来はメタデータAPIを使わないとできなかったSalesforceサーバーに対する様々なオペレーションをコマンドラインから実行できます。CLIの使い方についてはLWC入門(4)で紹介しています。
Mac または Windows環境への開発環境の導入手順についてはこちらを参照してみて下さい。また開発環境でのコンポーネント開発を効率化するために、こちらの手順に従って開発組織に対してクライアントキャッシュの無効化とデバッグモードの有効化の設定を行ってください。
*Lightningコンポーネントの開発を行うためには組織の「私のドメイン」の設定が必要です。
サンプル・コンポーネント
今回作成するコンポーネントは下記のようにログインユーザーの情報を表示するシンプルなものです。
比較のために、同じ機能をAuraコンポーネントとして実装したコードを載せておきます。
2| implements="flexipage:availableForAllPageTypes">
3| <aura:attribute name="record" type="Object" description="record data" />
4|
5| <!-- Event handler -->
6| <aura:handler name="init" value="{!this}" action="{!c.doInit}" />
7|
8| <!-- Section -->
9| <article class="slds-card">
10| <div class="slds-card__header slds-grid">
11| <div class="slds-media">
12| <div class="slds-media__figure">
13| <span class="slds-icon_container slds-icon-standard-opportunity" >
14| <lightning:icon iconName="standard:user" size="small" />
15| </span>
16| </div>
17| <div class="slds-media__body">
18| <span class="slds-card__header-title slds-truncate slds-m-right_xx-small">
19| ユーザー情報 (Aura)</span>
20| </div>
21| </div>
22| </div>
23| <div class="slds-card__body slds-card__body_inner">
24| <dl class="slds-list_horizontal slds-wrap">
25| <dt class="slds-item_label slds-text-color_weak slds-truncate" >名前:</dt>
26| <dd class="slds-item_detail slds-truncate" >
27| <lightning:formattedText value="{! v.record.Name}" />
28| </dd>
29| <dt class="slds-item_label slds-text-color_weak slds-truncate" >アカウント:</dt>
30| <dd class="slds-item_detail slds-truncate" >
31| <lightning:formattedText value="{! v.record.Username}" />
32| </dd>
33| <dt class="slds-item_label slds-text-color_weak slds-truncate" >ロール:</dt>
34| <dd class="slds-item_detail slds-truncate" >
35| <lightning:formattedText value="{! v.record.UserRole.Name}" />
36| </dd>
37| <dt class="slds-item_label slds-text-color_weak slds-truncate" >プロファイル:</dt>
38| <dd class="slds-item_detail slds-truncate" >
39| <lightning:formattedText value="{! v.record.Profile.Name}" />
40| </dd>
41| <dt class="slds-item_label slds-text-color_weak slds-truncate" t>種別:</dt>
42| <dd class="slds-item_detail slds-truncate" >
43| <lightning:formattedText value="{! v.record.UserType}" />
44| </dd>
45| </dl>
46| </div>
47| </article>
48| </aura:component>
[CMPファイル]
2| doInit : function(cmp, event, helper) {
3| // ユーザー情報を取得
4| var action = cmp.get('c.retrieveUserInfo');
5| action.setParams({
6| "userId": $A.get("$SObjectType.CurrentUser.Id")
7| });
8| action.setCallback(this, function (response) {
9| //エラー処理は省略
10| var data = response.getReturnValue();
11| cmp.set('v.record', data);
12| });
13| $A.enqueueAction(action);
14| }
15| })
[JSファイル]
DXプロジェクトの作成
コンポーネントの開発を始めるには、まず下記のCLIコマンドでローカル環境にDXプロジェクトを作成してください(lwcProjectはプロジェクトの名前です)。
sfdx force:project:create -n lwcProject
下記のようなフォルダ構成のプロジェクトが作成されます。defaultフォルダの下にApexクラスを格納する classesフォルダを追加しておいてください(自動では作成されないため)。
Lightning Web Componentsの作成
新規のコンポーネントは下記のCLIコマンドで作成します(プロジェクトのルートフォルダ(上記の例だとlwcProjectフォルダ)でコマンドを実行しないと正しい場所にファイルが作成されません)。
sfdx force:lightning:component:create --type lwc -n コンポーネント名 -d force-app/main/default/lwc
lwcフォルダの下に、指定したコンポーネント名のフォルダが作成されます。htmlファイル、javascriptファイル、メタデータファイル の3つのファイルがフォルダ配下に生成されています。CSSファイルはオプションなのでデフォルトでは作成されません。必要な場合は手作業で追加する必要があります。ネーミングルールは コンポーネント名.css です。
CLIコマンドを使わずに、VisualStudio Code からコンポーネントを作成することもできます。lwcフォルダで右クリックすると、メニューの一番下部に”SFDX: Create Lightning Web Component"が表示されますので、ここをクリックしてコンポーネント名を指定します。なお、DXプロジェクトのルートフォルダ(sfdx-project.json が配置されるフォルダ)でプロジェクトを開かないとこのメニューは表示されないことに注意してください。
メタデータ・ファイルの変更
自動生成されたメタデータ・ファイルに対して下記を修正してください。
- isExposed を true にする (AppBuilderでレコードページに配置するため)
- targetを追加する(下記の例ではレコードページ上に表示)
2| <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="lwc_userInfo">
3| <apiVersion>45.0</apiVersion>
4| <isExposed>true</isExposed>
5| <targets>
6| <target>lightning__RecordPage</target>
7| </targets>
8| </LightningComponentBundle>
[メタデータファイル]
HTMLファイルの実装
Auraコンポーネントとの違いは、LWCではシンプルにHTMLテンプレートのみを記述することです。HTMLを拡張する表記法を比べると、プロパティの参照方法、IF文、繰り返し、Lightningコンポーネントの表記法など細かい点でAuraコンポーネントと差異があります。
2| <article class="slds-card">
3| <div class="slds-card__header slds-grid">
4| <div class="slds-media">
5| <div class="slds-media__figure">
6| <span class="slds-icon_container slds-icon-standard-opportunity">
7| <lightning-icon icon-name="standard:user" size="small"></lightning-icon>
8| </span>
9| </div>
10| <div class="slds-media__body">
11| <span class="slds-card__header-title slds-truncate slds-m-right_xx-small">
12| ユーザー情報(lwc1) </span>
13| </div>
14| </div>
15| </div>
16| <div class="slds-card__body slds-card__body_inner">
17| <template if:true={record.data}>
18| <dl class="slds-list_horizontal slds-wrap">
19| <dt class="slds-item_label slds-text-color_weak slds-truncate" >名前:</dt>
20| <dd class="slds-item_detail slds-truncate" t>
21| <lightning-formatted-text class="font-red" value={record.data.Name}>
22| </lightning-formatted-text>
23| </dd>
24| <dt class="slds-item_label slds-text-color_weak slds-truncate" >アカウント:</dt>
25| <dd class="slds-item_detail slds-truncate" >
26| <lightning-formatted-text value={record.data.Username}></lightning-formatted-text>
27| </dd>
28| <template if:true={record.data.UserRole}>
29| <dt class="slds-item_label slds-text-color_weak slds-truncate" >ロール:</dt>
30| <dd class="slds-item_detail slds-truncate" >
31| <lightning-formatted-text value={record.data.UserRole.Name}>
32| </lightning-formatted-text>
33| </dd>
34| </template>
35| <dt class="slds-item_label slds-text-color_weak slds-truncate" >プロファイル:</dt>
36| <dd class="slds-item_detail slds-truncate" >
37| <lightning-formatted-text value={record.data.Profile.Name}></lightning-formatted-text>
38| </dd>
39| <dt class="slds-item_label slds-text-color_weak slds-truncate" >種別:</dt>
40| <dd class="slds-item_detail slds-truncate" >
41| <lightning-formatted-text value={record.data.UserType}></lightning-formatted-text>
42| </dd>
43| </dl>
44| </template>
45| </div>
46| </article>
47| </template>
[htmlコード全体]
Auraコンポーネントとの違いで注意して欲しいのは、プロパティを参照する {record.data.Name} の部分は、record.data の値がundefined(データの取得に失敗した場合)だとエラーになることです。Auraコンポーネントでは {!v.record.Name} で表記されたコードは属性recordがundefinedでもエラーになりませんでした。サンプルコードでは、データ取得に失敗した時にJavaScriptエラーが発生しないようにするために、下記のIF文を入れて表示を制御しています。
17| <template if:true={record.data}>
JavaScriptファイルの実装
JavaScriptコードの実装形式は大きく変わっています。Auraコンポーネントではcontrollerとhelperファイルに関数を定義していく形でしたが、LWCではクラスとしてコンポーネントの要素(プロパティ、getter/setter、イベント処理)を定義します。また、Salesforceサーバーからのデータの取得方式が大きく変更されており、より簡単な方法で実装できるように進化しています。サンプルコードでは、プロパティ形式のWire Serviceを使ってApexクラスを呼び出してデータを取得しています。
2|
3| //必ずインポートする
4| import { LightningElement, wire } from 'lwc';
5|
6| //Apex メソッドの定義
7| import retrieveUserInfo from '@salesforce/apex/lwc_UserInfoController.retrieveUserInfo';
8| //ログインユーザーIDの取得
9| import userid from '@salesforce/user/Id';
10|
11| export default class lwc_userInfo extends LightningElement {
12|
13| //Wire ServiceによるUserレコードの取得(プロパティ方式)
14| @wire(retrieveUserInfo, { 'userId': userid })
15| record;
16|
17| /*
18| * 初期化処理
19| */
20| connectedCallback() {
21| }
22|
23| }
[JavaScriptコード全体]
LWC固有の拡張機能を使用するために、lwcモジュールから共通機能をimportする必要があります。 LightningElement, wire, track,api などを必要に応じてimportします。また、参照するApexクラスは '@salesforce/apex/クラス名.メソッド名'の形式で import できます。
Apexクラスの実装
サーバー側Apexクラスの実装は、AuraコンポーネントとLWCで基本的に差異はありません。Wire Serviceを使う場合に cacheable=true が必要であることだけ覚えておいてください。
2| public with sharing class lwc_ContactController {
3| public lwc_ContactController() {
4|
5| }
6|
7| @AuraEnabled(cacheable=true)
8| public static Contact[] getContactList(){
9| return [SELECT Id, Name, Title, Picture__c, Phone, Email FROM Contact];
10| }
11| }
12|
[Apexクラス]
Salesforceサーバーへのアクセス
LWCではJSコンポーネントからSalesforceサーバーへのアクセス方法として下記の3つの方法が提供されています。実装するコード量は1 < 2 < 3 の順で増えますが、その分処理の自由度も上がります。単純にオブジェクトのデータを参照・更新するだけであれば、1. Lightning Data Service または 2. Wire Service を使うのが良いと思います。複雑なデータの更新処理や、任意のタイミングでサーバーアクセスを行いたい場合のみ、3のコード内から呼び出す実装方法を取ります。
Lightning Data Service(LDS)
LDSはAuraコンポーネントで提供されていた機能と同じで、サーバー側にコードを記述することなく、クライアントサイドのJavaScriptコードのみでSalesforceオブジェクトを取得可能です。提供されるAPIは随時更新されていますので、最新の状況については公式リファレンスを参照してください。
下記は同じlwc_userInfoコンポーネントをLDSを使って書き直したコードになります。lightning/uiRecordApi を使ってUserオブジェクトのレコードを参照しています。
2|
3| //必ずインポートする
4| import { LightningElement, wire } from 'lwc';
5| //LDSサービス
6| import { getRecord } from 'lightning/uiRecordApi';
7|
8| //ログインユーザーIDの取得
9| import userid from '@salesforce/user/Id';
10|
11| //取得対象のUserオブジェクトの項目
12| const FIELDS = [
13| 'User.Name',
14| // 'User.UserRole.Name', //クロスオブジェクト参照はサポートされない
15| // 'User.Profile.Name',
16| 'User.Username',
17| 'User.UserType'
18| ];
19|
20| export default class lwc_userInfo1 extends LightningElement {
21|
22| //WiringによるUserレコードの取得(プロパティ、Lightning Data Service利用)
23| @wire(getRecord, { 'recordId': userid, fields: FIELDS })
24| record;
25|
26| /*
27| * 初期化処理
28| */
29| connectedCallback() {
30| }
31|
32| }
[lwc_userInfo: LDSを利用]
取得したデータは Wire Serviceによりrecordプロパティにセットされ、HTMLからは {record.data.fields.XXX.value} (XXXは項目API名)形式で参照することができます。
Wire Service
Wire Serviceはデータ取得のコードを記述せずに、@Wireアノテーションを使った設定のみでサーバーからデータを取得できるLWCで導入された新しいデータ取得方式です。取得したデータを直接プロパティにセットするプロパティ方式とメソッドを呼び出すメソッド方式の2つが準備されています。
プロパティ方式のコード例はさきほど紹介したので、ここではメソッド方式のコードを紹介します。サーバー呼出の結果を引数として16行目のwiredUserInfo()関数が呼び出されます。下記の例では正常に値が取得できた場合はdataプロパティに値をセットし、失敗した場合はエラーメッセージをtoast形式で画面に表示するように実装しています。
2| import { LightningElement, wire, track } from 'lwc';
3| //toastメッセージ表示用の標準コンポーネント
4| import { ShowToastEvent } from 'lightning/platformShowToastEvent'
5|
6| //Apex メソッド
7| import retrieveUserInfo from '@salesforce/apex/lwc_UserInfoController.retrieveUserInfo';
8| //ログインユーザーIDの取得
9| import userid from '@salesforce/user/Id';
10|
11| export default class lwc_userInfo2 extends LightningElement {
12| @track data;
13|
14| //WiringによるUserレコードの取得(メソッド方式)
15| @wire(retrieveUserInfo, { 'userId': userid })
16| wiredUserInfo({ error, data }) {
17| if (data) {
18| this.data = data;
19| } else if (error) {
20| console.log('error:' + error.details.body.message);
21| this.showToast(error.details.body.message);
22| }
23| }
24|
25| /*
26| *toastによるメッセージ表示
27| */
28| showToast(message) {
29| const event = new ShowToastEvent({
30| title: 'エラーが発生しました',
31| message: message,
32| variant: 'error',
33| mode: 'pester'
34| });
35| this.dispatchEvent(event);
36| }
37| }
[lwc_userInfo:Wire Service メソッド方式]
コード内からの直接呼び出し
最後にJavaScriptコードで直接サーバー呼出を行う方法を紹介します。Auraコンポーネントでも同様なコードは記述していましたが、LWCではPromiseの仕組みを使うことでよりスマートに記述できるようになりました。
2| import retrieveUserInfo from '@salesforce/apex/lwc_UserInfoController.retrieveUserInfo';
3|
4| ・・・
5|
6| @track data;
7|
8| /*
9| * 初期化処理
10| */
11| connectedCallback() {
12| //メソッド直接呼び出しによるUserレコードの取得
13| retrieveUserInfo({ 'userId': userid })
14| .then(result => {
15| this.data = result;
16| })
17| .catch(error => {
18| this.showToast(error.body.message);
19| });
20| }
21|
22|
[lwc_userInfo:メソッド呼出しでデータを取得する]
おわりに
今回はLWCの基本的な実装方法について説明しました。Auraコンポーネントと比べると、HTMLとJavaScriptを使ってコンポーネントを開発する基本的な考え方は同様であるものの、実装方式はかなり変わっていると言えるでしょう。ただ、従来よりもシンプルかつ少ないコードで同等な機能を提供できるように工夫されている点が多数見受けられると感じました。Wire Serviceを使うことでAuraコンポーネントでは煩雑なコードになっていたサーバー呼出し部分をすっきりと書けるようになったことも大きな進化ではないでしょうか。import/exportの仕組みによりコードの再利用も促進されると思います。
■ Lightning Web Components 入門(全5回)シリーズは下記のリンクからご覧いただけます。
・ Lightning Web Components入門(1)Aura Components からLightning Web Componentsへ
・ Lightning Web Components入門(2)LWCの基本的な構成
・ Lightning Web Components入門(3)コンポーネントの実装方法
・ Lightning Web Components入門(4)Salesforce CLI
・ Lightning Web Components入門(5)応用編
著者について
Naoki Kitaarashi のコンテンツをもっと見る