Apex を使用したデータの操作
学習の目的
この単元を完了すると、次のことができるようになります。
- どのような場合に Apex を使用して Salesforce データを操作する必要があるかを認識する。
- 2 つの異なる方法で Apex をコールする。
- Apex と lightning-datatable を使用してレコードのリストを操作する。
Lightning Web コンポーネントでの Apex
Lightning データサービスのメリットと使用方法を学習しましたが、lightning-record-*-form コンポーネントも LDS ワイヤーアダプターや関数も適さない特定の使用事例がある場合もあります。たとえば、レコードデータが 1 件のトランザクションをカスタマイズする場合や、1 つのトランザクションで複数レコードの操作を実行する場合、Apex が最適です。
Lightning Web コンポーネントでの Apex の使用
Lightning Web コンポーネントで使用される Apex メソッドは static、public、global のいずれかで、メソッド定義の直前に @AuraEnabled アノテーションが付加されている必要があります。@AuraEnabled アノテーションによって Apex メソッドを Lightning コンポーネント (Lightning Web コンポーネントと Aura コンポーネントの両方) で使用できるようになります。
フレームワークがデータをキャッシュできるようにすることで、サーバーコールの繰り返しがなくなり、以降の参照操作の実行が速くなります。@AuraEnabled アノテーションに cacheable = true と設定すると、メソッドがキャッシュ可能とマークされます。@AuraEnabled メソッドがキャッシュ可能の場合、データ操作言語 (DML) による操作は許可されません。次の例の行 2 では、getContactsBornAfter メソッドがキャッシュ可能と指定されています。
ContactController.cls
public with sharing class ContactController {
@AuraEnabled(cacheable=true)
public static List<Contact> getContactsBornAfter(Date birthDate) {
return [
SELECT Name, Title, Email, Phone
FROM Contact
WHERE Birthdate > :birthDate
WITH SECURITY_ENFORCED
];
}
}メソッドがキャッシュ可能の場合、キャッシュが更新されるまで新しく追加または変更されたレコードのバージョンが返されません。次のセクションでキャッシュを手動で更新する方法を説明します。
LWC からの Apex メソッドコール
Lightning Web コンポーネントから Apex メソッドを操作するには、メソッドを結び付けるか、命令的にメソッドをコールするという 2 つの方法があります。両方のアプローチを考えてみましょう。
@wire を使用した Apex コール
Apex メソッドを結び付けるには、メソッドを cacheable にする必要があります。キャッシュ可能な Apex メソッドを結び付けるには、@wire デコレーターを使用します (LDS ワイヤーアダプターを使用する場合と同じ方法)。この方法で Apex をコールすると Lightning Web コンポーネントエンジンに制御が委任されてリアクティブサービスが作成されます。Apex メソッドに渡されるパラメーターの値が変わるたびに、Apex メソッドが実行され、デコレートされたプロパティまたは関数に新しい値がプロビジョニングされます。結び付けられたメソッドがキャッシュ可能である必要があるため、データは LDS キャッシュまたはサーバーから取得される可能性があります。Apex メソッドによってキャッシュされたデータを更新するには、refreshApex 関数をコールします。
注意: Lightning データサービスは、Apex メソッドによってキャッシュされたデータを認識しません。LDS 関数がレコードを更新すると、その更新は Apex メソッドによってキャッシュされたデータに影響を与えません。
次の例では @wire を使用して Apex をコールしています。このコードは、指定された生年月日より後に生まれた取引先責任者を取得します。
wireApexProperty.js
import { LightningElement, api, wire } from 'lwc';
import getContactsBornAfter from '@salesforce/apex/ContactController.getContactsBornAfter';
export default class WireApexProperty extends LightningElement {
@api minBirthDate;
@wire(getContactsBornAfter, { birthDate: '$minBirthDate' })
contacts;
}コードのポイント:
- 行 2:
getContactsBornAfter関数をContactControllerApex クラスからインポートします。これは対応する Apex メソッドを参照します。 - 行 4:
@api minBirthDateプロパティを定義します。このコンポーネントをコード内で使用したり、FlexiPage 属性を公開したりする場合は、日付を@api minBirthDateプロパティに渡すことができます。 - 行 5:
@wireデコレーターが 2 つのパラメーター (コールする Apex メソッド (getContactsBornAfter) とアダプターに必要なパラメーター (birthDate)) を受け取ります。$minBirthDateをリアクティブ変数 (先頭が$) として渡します。 - 行 6: 結果を
contactsプロパティに保存します。 - 行 5 ~ 6: 最初に Apex メソッドがデータを
contactsプロパティにプロビジョニングし、そのデータを LDS キャッシュに保存します。$minBirthDateはリアクティブであるため、その値が変更されるたびに Apex メソッドが実行され、新しいデータがキャッシュまたはサーバーからプロビジョニングされます。
命令的な Apex コール
@wire を使用して Apex をコールする代わりに、命令的に Apex をコールすることもできます。参照操作の呼び出しを制御する必要がある場合とレコードを変更する場合は、命令的に Apex をコールします。Apex を命令的にコールするには、インポートした関数をコンポーネントの JavaScript ファイルから呼び出します。この関数は JavaScript promise を返します (LDS 関数を命令的にコールする場合と同様)。
キャッシュ可能な Apex メソッドとキャッシュ不能な Apex メソッドの両方を命令的にコールすることができます。ただし、キャッシュ可能な Apex メソッドを命令的に更新することはできません。代わりに、@wire を使用してメソッドをコールし、refreshApex で更新します。
callApexImperative.js の例では、ユーザーが .html ファイル内の lightning-button (ここには含まれていない) をクリックすると、handleButtonClick が命令的に getContactsBornAfter Apex メソッドをコールします。
callApexImperative.js
import { LightningElement, api, wire } from 'lwc';
import getContactsBornAfter from '@salesforce/apex/ContactController.getContactsBornAfter';
export default class CallApexImperative extends LightningElement {
@api minBirthDate;
handleButtonClick() {
getContactsBornAfter({ //imperative Apex call
birthDate: this.minBirthDate
})
.then(contacts => {
//code to execute if related contacts are returned successfully
})
.catch(error => {
//code to execute if related contacts are not returned successfully
});
}
}コードのポイント:
- 行 2:
getContactsBornAfter関数をContactControllerクラスからインポートします。 - 行 4:
minBirthDate公開プロパティを定義します。これで、このコンポーネントをコードで使用したり、FlexiPage 属性を公開したりする際に日付を渡すことができるようになります。 - 行 6 ~ 7:
handleButtonClickメソッドがフレームワークによって呼び出されると、getContactsBornAfterApex メソッドを命令的に呼び出し、メソッドが指定された生年月日より後に生まれた取引先責任者を取得するのに必要なbirthDateを渡します。 - 行 9 ~ 14: 行 6 の命令的な Apex コールが promise を返します。Apex メソッドコールが成功した場合、promise が満たされ、
thenメソッドが実行されます。それ以外の場合、promise は却下され、catchメソッドが実行されます。
Lightning Web サービスでレコードのリストを操作するのに適しているのは、lightning-datatable 基本コンポーネントを使用する方法です。lightning-datatable を使用して、無限スクロール、インライン編集、ヘッダーおよび行レベルのアクション、サイズ変更などの機能を持つデータのテーブルを作成します。UI コンポーネントにはデータが入力されている必要があります。そのデータを生成するには、このモジュールですでに説明した方法のいずれかで Apex をコールするのが最も一般的な方法です。
レコードをテーブルにリストする Lightning Web コンポーネントのリリース
lightning-datatable に既存の取引先のリストを表示する例を操作しましょう。Apex と @wire を使用してレコードを取得します。
AccountControllerという名前の Apex クラスを作成します。
- エクスプローラーペインで、classes フォルダーを右クリックして [SFDX: Create Apex Class (SFDX: Apex クラスを作成)] を選択します。
- クラス名として
「AccountController」と入力し、Enter キーを押します。 - もう一度 Enter キーを押して、デフォルトのディレクトリを受け入れます。
- AccountController クラスの内容を次のコードで置き換えます。コードのポイント:
public with sharing class AccountController { @AuraEnabled(cacheable=true) public static List<Account> getAccounts() { return [ SELECT Name, AnnualRevenue, Industry FROM Account WITH SECURITY_ENFORCED ORDER BY Name ]; } }
- 行 2: メソッドに
@AuraEnabled(cacheable=true)アノテーションを付加して、結果がキャッシュされるようにします。 - 行 3: Apex で
getAccountsメソッドを定義して、参照操作を実行して既存の取引先を取得します。
accountListという名前の Lightning Web コンポーネントを作成します。- accountList.js ファイルの内容を次のコードで置き換えます。コードのポイント:
import { LightningElement, wire } from 'lwc'; import NAME_FIELD from '@salesforce/schema/Account.Name'; import REVENUE_FIELD from '@salesforce/schema/Account.AnnualRevenue'; import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry'; import getAccounts from '@salesforce/apex/AccountController.getAccounts'; const COLUMNS = [ { label: 'Account Name', fieldName: NAME_FIELD.fieldApiName, type: 'text' }, { label: 'Annual Revenue', fieldName: REVENUE_FIELD.fieldApiName, type: 'currency' }, { label: 'Industry', fieldName: INDUSTRY_FIELD.fieldApiName, type: 'text' } ]; export default class AccountList extends LightningElement { columns = COLUMNS; @wire(getAccounts) accounts; }
- 行 2 ~ 4: 以前の例と同様に項目参照をインポートします。
- 行 5:
getAccounts関数を AccountController クラスからインポートします。 - 行 13:
@wireをgetAccounts関数に使用してデータを取得します。 - 行 14: 結果を
accountsプロパティに保存します。操作が成功すると、レコードはaccounts.dataでアクセス可能になります。失敗するとエラーがaccount.errorに表示されます。
- accountList.html ファイルの内容を次のコードで置き換えます。コードのポイント:
<template> <lightning-card> <template if:true={accounts.data}> <lightning-datatable key-field="Id" data={accounts.data} columns={columns} > </lightning-datatable> </template> </lightning-card> </template>
- 行 4 ~ 9:
lightning-datatable基本コンポーネントを定義して、JavaScript ファイルに入力されているaccounts.dataとcolumnsを使用します。
- AccountController クラスを保存します。
- accountList.js-meta.xml の内容を次のコードで置き換えてコンポーネントをアプリケーションページで使用できるようにします。
<?xml version="1.0" encoding="UTF-8"?> <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata"> <apiVersion>48.0</apiVersion> <isExposed>true</isExposed> <targets> <target>lightning__AppPage</target> </targets> </LightningComponentBundle> - 3 つのコンポーネントファイルをすべて保存します。
- force-app/main/default フォルダーを Trailhead Playground にリリースします。
- Trailhead Playground で、Lightning アプリケーションビルダーに移動し、[Working with Data (データの操作)] ページを開きます。
- accountList コンポーネントをページのメインの範囲にドラッグします。
- ページを保存します。
- [Working with Data (データの操作)] ページに戻って新しいコンポーネントを表示します。
Lightning Web コンポーネントで Salesforce データを操作するいくつかの方法を理解できました。次は、サーバーエラーが発生したときの処理方法を学習します。
リソース
