force:recordData を使用したレコードの操作

学習の目的

この単元を完了すると、次のことができるようになります。

  • Aura コンポーネントのための Lightning データサービスを使用してレコードの作成、参照、アップロード、削除を行う。
  • force:recordData を使用するコンポーネントを作成する。
  • Lightning データサービスでレコードをキャッシュする方法を説明する。

force... recordData タグの使用

前の単元では、便利なパフォーマンスアップグレードと、Lightning データサービスが提供するクオリティオブライフ機能について説明しました。では、これらの使用方法について学習しましょう。

本質的に force:recordData には UI 要素が含まれません。force:recordData タグは、サーバとの通信およびローカルキャッシュの管理を行うために使用する単なるロジックです。LDS で取得したデータをユーザが表示および変更する場合、UI 要素を含める必要があります。force:recordData タグは、UI API を使用してデータを UI コンポーネントに提供します。

force:recordData を使用する場合は、データを一度読み込んで、子コンポーネントに属性として渡します。このアプローチでは、リスナーの数が減り、サーバコールが最小限になるため、パフォーマンスが向上し、コンポーネントに一貫したデータが表示されます。

レコードの読み込み

UI コンポーネントでレコードを使用できるようにするには、まずレコードを読み込みます。レコードを読み込むには、recordIdmode、および layoutType または fields 属性を指定するときにコンポーネントに force:recordData を含めます。layoutType の有効な値は、FullCompact です。

ldsDisplayRecord.cmp

<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId"> <!--inherit recordId attribute-->
<aura:attribute name="record" type="Object"
  description="The record object to be displayed"/>
<aura:attribute name="simpleRecord" type="Object"
  description="A simplified view record object to be displayed"/>
<aura:attribute name="recordError" type="String"
  description="An error message bound to force:recordData"/>
<force:recordData aura:id="record"
    fields="Name,BillingCity,BillingState"
    recordId="{!v.recordId}"
    targetError="{!v.recordError}"
    targetRecord="{!v.record}"
    targetFields ="{!v.simpleRecord}"
    mode="VIEW"/>

パフォーマンスを向上させるには、fields 属性を使用して必要な項目のみを照会することをお勧めします。layoutType は、プロビジョニングされる項目をコンポーネントではなくシステム管理者が制御する場合のみ使用します。コンポーネントは、コンテキストユーザのレイアウトに割り当てられたすべての項目の受信を処理する必要があります。

次に、force:recordData で読み込まれたデータを表示する何らかのマークアップを含めます。

<!-- Display a lightning card with details about the record -->
<lightning:card iconName="standard:account" title="{!v.simpleRecord.Name}" >
    <div class="slds-p-horizontal--small">
        <p class="slds-text-heading--small">
            <lightning:formattedText title="Billing City" value="{!v.simpleRecord.BillingCity}" /></p>
        <p class="slds-text-heading--small">
            <lightning:formattedText title="Billing State" value="{!v.simpleRecord.BillingState}" /></p>
    </div>
</lightning:card>
<!-- Display Lightning Data Service errors, if any -->
<aura:if isTrue="{!not(empty(v.recordError))}">
    <div class="recordError">
        {!v.recordError}
    </div>
</aura:if>
</aura:component>

ldsDisplayRecord カスタムコンポーネントによって flexipage:availableForRecordHomeforce:hasRecordId が実装されるため、Lightning Experience のレコードページの作成および設定で説明している手順に従って、Lightning アプリケーションビルダーを使用してコンポーネントをレコードページに追加できます。Lightning アプリケーションビルダーでは、ldsDisplayRecord は次のように表示されます。

Lightning アプリケーションビルダーでの ldsDisplayRecord コンポーネント

flexipage:availableForRecordHome インターフェースは、Lightning アプリケーションビルダーでレコードページにカスタムコンポーネントを使用できるようにします。force:hasRecordId インターフェースは、カスタムコンポーネントに、そのコンポーネントを含むレコードページのレコード ID を提供します。Lightning データサービスによって提供されるレコードデータを使用して、カスタムユーザインターフェースを作成できます。

レコードの保存

Lightning アプリケーションに複数のコンポーネントがあり、それらが同じレコードデータから情報を取得している場合、LDS は魔法を起こします。一部のコンポーネントはレコードデータを表示するだけですが、他のコンポーネントはデータ自体を操作できます。このコンポーネントは、ユーザがレコードの新しい名前を入力できる簡単なフォームと共にレコードを読み込みます。

ldsSaveRecord.cmp

<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId"> <!--inherit recordId attribute-->
<aura:attribute name="record" type="Object" />
<aura:attribute name="simpleRecord" type="Object" />
<aura:attribute name="recordError" type="String" />
<force:recordData aura:id="recordEditor"
    fields="Name,BillingCity,BillingState"
    recordId="{!v.recordId}"
    targetError="{!v.recordError}"
    targetRecord="{!v.record}"
    targetFields ="{!v.simpleRecord}"
    mode="EDIT" />
    <!-- Display a lightning card with details about the record -->
    <lightning:card iconName="standard:account" title="{!v.simpleRecord.Name}" >
        <div class="slds-p-horizontal--small">
            <p class="slds-text-heading--small">
                <lightning:formattedText title="Billing State" value="{!v.simpleRecord.BillingState}" /></p>
            <p class="slds-text-heading--small">
                 <lightning:formattedText title="Billing City" value="{!v.simpleRecord.BillingCity}" /></p>
        </div>
    </lightning:card>
    <br/>
    <!-- Display an editing form -->
    <lightning:card iconName="action:edit" title="Edit Account">
        <div class="slds-p-horizontal--small">
            <lightning:input label="Account Name" value="{!v.simpleRecord.Name}"/>
            <br/>
            <lightning:button label="Save Account" variant="brand" onclick="{!c.handleSaveRecord}" />
        </div>
    </lightning:card>
    <!-- Display Lightning Data Service errors, if any -->
    <aura:if isTrue="{!not(empty(v.recordError))}">
        <div class="recordError">
            {!v.recordError}
        </div>
    </aura:if>
</aura:component>

この更新を処理するには、saveRecord() メソッドをコールする JavaScript コントローラを作成します。saveRecord() メソッドは唯一のパラメータとして 1 つのコールバック関数 SaveRecordResult を使用します。SaveRecordResult には、保存が成功したかどうかを示す state 属性と、操作結果を処理するために使用できるその他の情報が含まれます。

ldsSaveRecordController.js

({
    handleSaveRecord: function(component, event, helper) {
        component.find("recordEditor").saveRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                console.log("Save completed successfully.");
            } else if (saveResult.state === "INCOMPLETE") {
                console.log("User is offline, device doesn't support drafts.");
            } else if (saveResult.state === "ERROR") {
                console.log('Problem saving record, error: ' +
                           JSON.stringify(saveResult.error));
            } else {
                console.log('Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error));
            }
        }));}
})

悪くないですよね。LDS は舞台裏で行われるすべての力仕事を処理します。つまり、要求をサーバに送信し、両方のレコードを自動的に更新します。

ldsDisplayRecord コンポーネントと同様に、ldsSaveRecord コンポーネントは Lightning アプリケーションビルダーを使用してレコードページに追加できます。

では、残りの CRUD について見てみましょう。

レコードの作成

空のレコードを作成するには、force:recordDatarecordId 属性を未定義のままにします。

ldsNewRecord.cmp

<aura:component implements="flexipage:availableForRecordHome, force:hasRecordId">
<aura:attribute name="newContact" type="Object"/>
<aura:attribute name="simpleNewContact" type="Object"/>
<aura:attribute name="newContactError" type="String"/>
<force:recordData aura:id="contactRecordCreator"
    fields="FirstName,LastName,Title"
    targetRecord="{!v.newContact}"
    targetFields ="{!v.simpleNewContact}"
    targetError="{!v.newContactError}"
    />
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <!-- Display the new contact form -->
    <lightning:card iconName="action:new_contact" title="Create Contact">
        <div class="slds-p-horizontal--small">
            <lightning:input aura:id="contactField" label="First Name" value="{!v.simpleNewContact.FirstName}"/>
            <lightning:input aura:id="contactField" label="Last Name" value="{!v.simpleNewContact.LastName}"/>
            <lightning:input aura:id="contactField" label="Title" value="{!v.simpleNewContact.Title}"/>
            <br/>
            <lightning:button label="Save Contact" variant="brand" onclick="{!c.handleSaveContact}"/>
        </div>
    </lightning:card>
    <!-- Display Lightning Data Service errors -->
    <aura:if isTrue="{!not(empty(v.newContactError))}">
        <div class="recordError">
            {!v.newContactError}
        </div>
    </aura:if>
</aura:component>

コンポーネントコントローラで getNewRecord() メソッドをコールします。ユーザがレコードを作成したら、上記の saveRecord() メソッドを使用してレコードを保存します。

メモ

メモ

選択リスト、チェックボックス、ラジオボタンなど、テキスト項目以外を処理する場合はどうすればよいのでしょうか? まず、前の単元で説明した Form ベースのコンポーネントを使用することをお勧めします。レイアウト、入力規則、CRUD 変更、エラー処理に加え、標準機能として項目の対応付けがあるためです。force:recordData では、必要な Salesforce 項目にコンポーネントを結び付ける必要があります。

ldsNewRecordController.js

({
    doInit: function(component, event, helper) {
        // Prepare a new record from template
        component.find("contactRecordCreator").getNewRecord(
            "Contact", // sObject type (entityAPIName)
            null,      // recordTypeId
            false,     // skip cache?
            $A.getCallback(function() {
                var rec = component.get("v.newContact");
                var error = component.get("v.newContactError");
                if(error || (rec === null)) {
                    console.log("Error initializing record template: " + error);
                }
                else {
                    console.log("Record template initialized: " + rec.apiName);
                }
            })
        );
    },
    handleSaveContact: function(component, event, helper) {
        if(helper.validateContactForm(component)) {
            component.set("v.simpleNewContact.AccountId", component.get("v.recordId"));
            component.find("contactRecordCreator").saveRecord(function(saveResult) {
                if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                    // record is saved successfully
                    var resultsToast = $A.get("e.force:showToast");
                    resultsToast.setParams({
                        "title": "Saved",
                        "message": "The record was saved."
                    });
                    resultsToast.fire();
                } else if (saveResult.state === "INCOMPLETE") {
                    // handle the incomplete state
                    console.log("User is offline, device doesn't support drafts.");
                } else if (saveResult.state === "ERROR") {
                    // handle the error state
                    console.log('Problem saving contact, error: ' +
                                 JSON.stringify(saveResult.error));
                } else {
                    console.log('Unknown problem, state: ' + saveResult.state +
                                ', error: ' + JSON.stringify(saveResult.error));
                }
            });
        }
    }
})

ldsNewRecord カスタムコンポーネントは、新しい取引先責任者レコードを作成し、取引先責任者レコードでは [取引先名] (AccountId) 項目が必須です。ldsNewRecord カスタムコンポーネントを取引先レコードページに追加する場合、v.recordId 属性は ldsNewRecord が置かれている取引先レコードページのレコード ID を返します。component.set("v.simpleNewContact.AccountId", component.get("v.recordId")); の行は、AccountId 項目に取引先レコード ID を設定します。

このヘルパーはフォームの値を検証するものです。

ldsNewRecordHelper.js

({
    validateContactForm: function(component) {
        var validContact = true;
         // Show error messages if required fields are blank
        var allValid = component.find('contactField').reduce(function (validFields, inputCmp) {
            inputCmp.showHelpMessageIfInvalid();
            return validFields && inputCmp.get('v.validity').valid;
        }, true);
        if (allValid) {
            // Verify we have an account to attach it to
            var account = component.get("v.newContact");
            if($A.util.isEmpty(account)) {
                validContact = false;
                console.log("Quick action context doesn't have a valid account.");
            }
        return(validContact);
        }
	}
})

レコードの削除

最後に、レコードを削除するには、少なくとも fields 属性を「Id」に設定して recordId を指定します。

ldsDeleteRecord.cmp

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">
<aura:attribute name="recordError" type="String" access="private"/>
<force:recordData aura:id="recordHandler"
    recordId="{!v.recordId}"
    fields="Id"
    targetError="{!v.recordError}"
    />
    <!-- Display the delete record form -->
    <lightning:card iconName="action:delete" title="Delete Record">
        <div class="slds-p-horizontal--small">
            <lightning:button label="Delete Record" variant="destructive" onclick="{!c.handleDeleteRecord}"/>
        </div>
    </lightning:card>
    <!-- Display Lightning Data Service errors, if any -->
    <aura:if isTrue="{!not(empty(v.recordError))}">
        <div class="recordError">
            {!v.recordError}
        </div>
    </aura:if>
</aura:component>

コンポーネントの JavaScript コントローラで deleteRecord() メソッドをコールします。LDS はキャッシュからレコードを削除して、通知を起動します。deleteRecord() メソッドは、saveRecord() メソッドに似たコールバック関数 (deleteRecordResult) を使用して、操作が成功したかどうかを示します。

ldsDeleteRecordController.js

({
    handleDeleteRecord: function(component, event, helper) {
    component.find("recordHandler").deleteRecord($A.getCallback(function(deleteResult) {
        if (deleteResult.state === "SUCCESS" || deleteResult.state === "DRAFT") {
            console.log("Record is deleted.");
            var resultsToast = $A.get("e.force:showToast");
            resultsToast.setParams({
                "title": "Deleted",
                "message": "The record was deleted."
            });
            resultsToast.fire();
        }
        else if (deleteResult.state === "INCOMPLETE") {
            console.log("User is offline, device doesn't support drafts.");
        }
        else if (deleteResult.state === "ERROR") {
            console.log('Problem deleting record, error: ' +
                        JSON.stringify(deleteResult.error));
        }
        else {
            console.log('Unknown problem, state: ' + deleteResult.state +
                        ', error: ' + JSON.stringify(deleteResult.error));
        }
    }));
}})

レコードの非同期保存

では仮定の状況を考えてみましょう。あなたは Salesforce アプリケーションを使用しています。接続に問題があるため保存の試みがサーバに到達しません。乗っている電車がトンネルに入ったのかもしれません。たまたまビルの隅に入り込んでしまい携帯電波を受信できないのかもしれません。あるいはグレムリンがまた電波塔をめちゃくちゃにしているのかもしれません。いずれにしても心配はいりません。LDS があなたをサポートします。接続に問題が発生した場合、Lightning データサービスは変更内容をローカルキャッシュに保存します。これは、SaveRecordResult オブジェクトの DRAFT 状態によって示されます。接続が回復すると、レコードの DRAFT 状態は解決されます。LDS を通じてレコードを保存している場合、保存が完了するまでローカルキャッシュは更新されません。サーバへの保存が正常に完了すると、キャッシュはサーバにあるレコードの最新バージョンに更新され、そのレコードを参照するすべてのコンポーネントに通知されます。保存後、レコードをキャッシュに手動で再読み込みする心配はいりません。LDS がすべてを自動で処理します。

非同期保存の権限を有効にしている場合、または以下のすべてに該当する場合、デバイスがオフラインのときに実行された保存は DRAFT 状態になります。

  • クライアントがサーバにアクセスできない。
  • 組織でオフラインドラフトを有効にしている。
  • バージョン 9.0 以降の Salesforce アプリケーションを使用している。

すべての CRUD 操作は、サーバへの XMLHttpRequest を使用して直ちに解決しようとします。デバイスがサーバへの接続を失っても、Lightning データサービスはローカルキャッシュからデータを取得できます。LDS がローカルキャッシュまたはサーバのどちらからデータを取得するかは、レコードがどれだけ古いか、またはローカルドラフトが存在するかどうかによって決まります。レコードが十分に新しい場合、またはローカルドラフトが存在する場合、LDS はローカルキャッシュを使用します。LDS は必要な場合にのみレコードを更新します。つまり、すべての更新はコンポーネントによってトリガされます。

Trailhead Playground の作成

耳寄りなお知らせです。無料の Trailhead Playground (TP) 組織で LDS の使用方法を実践できます。TP とは何でしょうか? Trailhead 用にカスタマイズされた Salesforce Developer Edition 組織です。どのハンズオン課題からも TP を起動 (または新しい TP を作成) できます。ここでは新しい TP を作成します (既存の組織を使用すると、課題を確認するときに問題が発生する可能性があります)。このページの一番下までスクロールします。[起動] の横にある下矢印をクリックし、[Trailhead Playground を作成] を選択します (ログインが必要です)。新しい TP のログイン情報が必要な場合は、こちらの記事の手順に従います。

リソース