Salesforce データの操作

学習の目的

この単元を完了すると、次のことができるようになります。
  • 基本コンポーネントを使用して、1 つのレコードを処理するフォームを作成する。
  • ワイヤサービスを使用してデータを取得する。
  • データを作成して更新する。
  • Apex メソッドをコールする。

データのないコンポーネントは見た目がすっきりしているかもしれませんが、空っぽに感じられます。この単元では、こうした空虚感を埋め、Aura コンポーネントと Lightning コンポーネントの Salesforce データの読み取り方法と書き込み方法を比較します。

Aura コンポーネントの Apex クラスを開発している場合は、Lightning Web コンポーネントでも Apex を再利用できます。ただし、Lightning Web コンポーネントにはデータを処理する別の手段が用意されているため、開発した Apex が不要になる場合もあります。

1 つのレコードを処理するフォームの作成

ユーザが Salesforce レコードを表示、編集、作成できるフォームを作成する場合、Aura コンポーネントと Lightning Web コンポーネントのどちらも lightning 名前空間で基本コンポーネントを使用します。

これらのコンポーネントにはフォームレイアウトがあり、レコードの CRUD 変更が処理されるため、Apex コードを記述する必要がありません。Lightning データサービスを使用して、レコードの更新をキャッシュし、コンポーネント間で共有します。

Lightning データサービスは公開ユーザインターフェース API 上に構築されていますが、API のサブセットしかサポートしていません。このサブセットが、データを処理する一般的なユースケースの多くに対処します。

Aura コンポーネントと Lightning Web コンポーネントでは、動作は同じですが、前述のとおり命名規則が異なります。

Form 関数 Aura コンポーネント Lightning Web コンポーネント
編集、表示、参照のみモードをサポート lightning:recordForm lightning-record-form
参照のみのフォーム lightning:recordViewForm lightning-record-view-form
編集可能なフォーム lightning:recordEditForm lightning-record-edit-form

大半のユースケースでは、lightning-record-form から始めるとよいでしょう。このコンポーネントには lightning-record-view-form と lightning-record-edit-form の機能が結合され、簡単に使えるようになっています。

カスタム項目レイアウトやレコードデータのカスタム表示を要する高度なユースケースでは lightning-record-view-form と lightning-record-edit-form を使用します。

以下は、BrokerDetails Aura コンポーネントの例です。

<lightning:recordForm
  objectApiName="Broker__c"
  recordId="{!v.property.Broker__c}"
  fields="{!v.brokerFields}"
  columns="2"/>

以下は、対応する brokerCard Lightning コンポーネントの HTML です。

<lightning-record-form
  object-api-name="Broker__c" 
  record-id={brokerId}
  fields={brokerFields} 
  columns="2">
</lightning-record-form>

1 つのレコードを処理するカスタム UI

Aura コンポーネントで lightning:recordForm や同様の関数よりも UI に対する管理を強化する必要がある場合は、マークアップの独自のカスタム UI に <force:recordData> タグを使用できます。

PropertySummary Aura コンポーネントでは、マークアップに <force:recordData> を使用しています。

<force:recordData aura:id="service"
      recordId="{!v.recordId}"
      targetFields="{!v.property}"
      fields="['Id',
              'Thumbnail__c',
              'Address__c',
              'City__c',
              'State__c',
              'Zip__c',
              'Price__c',
              'Beds__c',
              'Baths__c',
              'Broker__r.Id',
              'Broker__r.Name',
              'Broker__r.Title__c',
              'Broker__r.Mobile_Phone__c',
              'Broker__r.Email__c',
              'Broker__r.Picture__c']" />

Lightning Web コンポーネントについては、<force:recordData> の代わりに、Lightning データサービスを使用するいくつかの技法が用意されていますが、データを読み込んでいるのか書き込んでいるのかによって使用する技法が異なります。ワイヤサービスや JavaScript API のメソッドは、lightning-record-*-form コンポーネントが各自の要件を満たさない場合にのみ使用を検討します。

ワイヤサービスを使用したデータの取得

Lightning Web コンポーネントでは、Salesforce データを読み取るために、Lightning データサービス上に構築されたリアクティブなワイヤサービスを使用します。コンポーネントは JavaScript クラスの @wire を使用して、lightning/ui*Api 名前空間のいずれかのワイヤアダプタからデータを読み取ります。Salesforce が提供するワイヤアダプタのリストについては、この単元の「リソース」セクションを参照してください。独自のカスタムワイヤアダプタを記述することはできません。

このワイヤサービスをリアクティブと呼ぶ理由の 1 つは、$ というプレフィックスが付いたリアクティブ変数をサポートしているためです。リアクティブ変数が変更されると、ワイヤサービスが新しいデータをプロビジョニングします。「要求」や「取得」ではなく「プロビジョニング」と言うのは、データが提供されたときに、そのデータをコンポーネントではなく、ワイヤアダプタが管理するためです。

以下は、@wire を使用してレコードを取得する propertySummary.js の例です。

import { LightningElement, api, wire } from 'lwc';
import { getRecord, getFieldValue } from 'lightning/uiRecordApi';
import NAME_FIELD from '@salesforce/schema/Property__c.Name';
import PICTURE_FIELD from '@salesforce/schema/Property__c.Picture__c';
export default class PropertySummary extends LightningElement {
    @api recordId;
    propertyName;
    pictureURL;
    @wire(getRecord, { recordId: '$recordId', fields: [NAME_FIELD, PICTURE_FIELD] })
    wiredRecord({ error, data }) {
        if (data) {
            this.propertyName = getFieldValue(data, NAME_FIELD);
            this.pictureURL = getFieldValue(data, PICTURE_FIELD);
        } else if (error) {
            // Handle error. Details in error.message.
        }
    }
}

このコードは lightning/uiRecordApi から getRecord ワイヤアダプタをインポートします。

ui*Api ワイヤアダプタを使用する場合は、オブジェクトや項目への参照をインポートすることを強くお勧めします。オブジェクトや項目への参照をインポートすると、オブジェクトや項目が存在することを Salesforce が検証するため、コードが確実に機能します。次のコードの行は、Property カスタムオブジェクトの Name 項目への参照をインポートします。

import NAME_FIELD from '@salesforce/schema/Property__c.Name';

@api デコレータは recordId プロパティを公開します。propertySummary を含む親コンポーネントは、その HTML ファイルの record-id 属性の値を設定します。

$recordId は先頭に $ が付いているため、その値が変更されると、ワイヤサービスが新しいデータを取得してコンポーネントにプロビジョニングします。新しいデータが提供されると、結び付けられている関数が呼び出されます。このプロセスがプロパティを更新し、それによって再表示が行われます。

wiredRecord 関数が、このワイヤサービスからデータのストリームを受信します。そしてレコードデータが data 引数に返されます。エラーがある場合は error 引数に返されます。

JavaScript API メソッドを使用したデータの書き込み

createRecord JavaScript API はレコードを作成します。また、updateRecord や deleteRecord を使用することもできます。私たちは今なお内部で Lightning データサービスを使用しているため、Apex は必要ありません。

重要

重要

レコードの作成、更新、削除に @wire を使用しないでください。ワイヤサービスはフローの管理を Lightning Web コンポーネントエンジンに委任します。管理の委任は読み取り操作では有益ですが、作成、更新、削除操作ではさほど役に立ちません。開発者であれば、データを変更する操作はすべて自分で管理したいと思うでしょう。作成、更新、削除操作を、ワイヤサービスではなく、JavaScript API で行うのはこのためです。

以下は、createRecord を使用して取引先を作成する ldsCreateRecord Lightning Web コンポーネントです。

import { LightningElement } from 'lwc';
import { ShowToastEvent } from 'lightning/platformShowToastEvent';
import { createRecord } from 'lightning/uiRecordApi';
import ACCOUNT_OBJECT from '@salesforce/schema/Account';
import NAME_FIELD from '@salesforce/schema/Account.Name';
export default class LdsCreateRecord extends LightningElement {
    accountId;
    name;
    onNameChange(event) {
        this.name = event.target.value;
    }
    createAccount() {
        const recordInput = {
            apiName: ACCOUNT_OBJECT.objectApiName,
            fields: { 
                [NAME_FIELD.fieldApiName]: this.name, 
            }
        };
        createRecord(recordInput)
            .then(account => {
                this.accountId = account.id;
                this.dispatchEvent(
                    new ShowToastEvent({
                        title: 'Success',
                        message: 'Account created',
                        variant: 'success',
                    }),
                );
            })
            .catch(error => {
                // Handle error. Details in error.message.
            });
    }
}

createRecord が、レコードが正常に作成されると解決する Promise オブジェクトを返します。JavaScript API についての詳細は、この単元の末尾の「リソース」を参照してください。

ldsCreateRecord の HTML ファイルに、JavaScript ファイルの createAccount() を呼び出すボタンが含まれます。取引先名が lightning-input コンポーネントに設定されます。

<template>
    <lightning-card title="LdsCreateRecord" icon-name="standard:record">
        <div class="slds-m-around_medium">
            <lightning-input label="Id" disabled value={accountId}></lightning-input>
            <lightning-input label="Name" onchange={onNameChange} class="slds-m-bottom_x-small"></lightning-input>
            <lightning-button label="Create Account" variant="brand" onclick={createAccount}></lightning-button>
        </div>
    </lightning-card>
</template>

カスタムデータへのアクセスでの Apex の使用

Lightning データサービスを使用してデータを処理する技法をいくつか見てきました。これらの技法は Apex を必要としないため、通常はコードが少なくてすみます。けれども、すでに Aura コンポーネント用に開発した Apex コードがある場合は、そのコードを Lightning Web コンポーネントでも再利用できます。また、カスタムデータにアクセスするために SOQL クエリが必要な場合は、Apex メソッドを使用する必要があります。

Aura コンポーネントから Apex コントローラにアクセスするには、JavaScript コントローラまたはヘルパーからコールを実行します。以下は、getPictures() Apex メソッドをコールする PropertyCarousel Aura コンポーネントのヘルパーです。

({
    loadPictures : function(component) {
        var propertyId = component.get("v.recordId");
        component.set("v.files", []);
        if (!propertyId) {
            return;
        }
        var action = component.get("c.getPictures");
        action.setParams({
            "propertyId": propertyId,
        });
        action.setCallback(this, function (response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                var files = response.getReturnValue();
                component.set("v.files", files);
            }
            else if (state === "INCOMPLETE") {
                // handle incomplete state
            }
            else if (state === "ERROR") {
                // handle error state
            }
        });
        $A.enqueueAction(action);
    }
})

Salesforce データにアクセスするどの Aura コンポーネントにも同様の定型コードがあります。Apex メソッドにアクセスする構文は、Lightning Web コンポーネントの場合とは異なります。

Lightning Web コンポーネントは Apex クラスからメソッドをインポートできます。インポートされたメソッドは、コンポーネントが @wire を使用して宣言的に、あるいはコードで直接コールできる関数です。

Apex メソッドの Lightning Web コンポーネントへの公開

Apex メソッドを Lightning Web コンポーネントに公開するには、メソッドが static で、かつ global または public のいずれかである必要があります。メソッドに @AuraEnabled アノテーションを付加します。こうした要件は、Aura コンポーネントで Apex メソッドを使用する場合と同じです。@AuraEnabled アノテーションは、Aura コンポーネントまたは Lightning Web コンポーネントからメソッドをコールできることを意味します。Lightning Web コンポーネントでの @AuraEnabled の使用は意外なように思えるかもしれませんが、このアノテーションを使用することで、同じ Apex コードを両方のプログラミングモデルで使用できるようになります。

ワイヤサービスを使用した Apex メソッドのコール

Apex メソッドがキャッシュ可能で (データを変更しない) 場合は、ワイヤサービスを使用してコンポーネントから呼び出すことができます。このメソッドには @AuraEnabled(cacheable=true) アノテーションを付加する必要があります。

メモ

メモ

データを作成、更新、削除する Apex メソッドには @wire を使用しないでください。

getAccounts() メソッドを指定した MyAccountController Apex コントローラを見てみましょう。

// MyAccountController.cls
public with sharing class MyAccountController {
    @AuraEnabled(cacheable=true)
    public static List<Account> getAccounts() {
        return [SELECT Id, Name FROM Account
            WHERE AnnualRevenue > 1000000];
    }
}

この JavaScript コードは、ワイヤサービスを使用して Apex getAccounts() メソッドをコールします。

import { LightningElement, wire } from 'lwc';
import getAccounts from '@salesforce/apex/MyAccountController.getAccounts';
export default class HelloApexAccounts extends LightningElement {
    accounts=[];
    @wire(getAccounts, {})
    wiredAccounts({ error, data }) {
        if (error) {
            this.error = error;
        } else if (data) {
            this.accounts = data;
        }
    }
}

getAccounts をインポートするステートメントの構文に注意します。

import getAccounts from '@salesforce/apex/MyAccountController.getAccounts';
  • MyAccountController は Apex クラスの名前です。
  • getAccounts はインポートする @AuraEnabled メソッドです。

Apex メソッドの直接的なコール

Apex メソッドがデータを変更 (作成、更新、削除) するため、キャッシュ可能でない場合は、メソッドを直接コードでコールする必要があります。

以下は、Apex getContactList() メソッドをコールする Lightning Web コンポーネントです。

import { LightningElement } from 'lwc';
import getContactList from '@salesforce/apex/ContactController.getContactList';
export default class ApexContactList extends LightningElement {
    contacts;
    getContacts() {
        getContactList()
            .then(result => {
                this.contacts = result;
            })
            .catch(error => {
                // Handle error. Details in error.message.
            });
    }
}

import ステートメントは、ワイヤサービスの例と同じように見えます。けれども、メソッドをコールする構文に @wire アノテーションがありません。代わりに getContactList() が、取引先責任者のリストが正常に作成されると解決する Promise オブジェクトを返します。

外部 API の使用

Lightning Web コンポーネントでの外部 API の使用は、Aura コンポーネントと同様です。デフォルトでは、Lightning Web コンポーネントの JavaScript コードからサードパーティの API にコールを実行することはできません。リモートサイトを CSP 信頼済みサイトとして追加し、JavaScript コンポーネントのコードがアセットを読み込み、そのサイトのドメインに API 要求を実行できるようにします。

サードパーティのサイトからの JavaScript コードの実行については、Aura コンポーネントも Lightning Web コンポーネントも制限は同じです。サードパーティのライブラリを使用するには、そのライブラリを静的リソースとしてアップロードする必要があります。

リソース