Lightning-数据服务(2)

学习目标

完成本单元后,您将能够:

  • 使用闪电数据服务创建,读取,上传和删除记录。
  • 构建一个使用force的组件:recordData。
  • 解释Lightning Data Service如何缓存记录。

使用force … recordData标签

在最后一个单元中,我们介绍了Lightning Data Service提供的漂亮的性能升级和生活质量功能。现在让我们学习如何使用它们。

请记住,force:recordData本身不包含任何UI元素。 force:recordData标签只是用来与服务器通信并管理本地缓存的逻辑。为了让用户查看和修改由LDS获取的数据,您必须包含UI元素。 force:recordData标记使用UI API向您的UI组件提供数据。

加载记录

你做的第一件事是为你的UI组件创建一个记录,就是加载它。通过在指定recordId,mode和layoutType或fields属性的同时在组件中包含force:recordData来加载记录。

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"
    layoutType="FULL"
    recordId="{!v.recordId}"
    targetError="{!v.recordError}"
    targetRecord="{!v.record}"
    targetFields ="{!v.simpleRecord}"
    mode="VIEW"/>
从这里,包含一些显示强制加载的数据的 force:recordData.
<!-- 显示有关记录详情的闪电卡 -->
<div class="Record Details"> 
    <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>
</div>

<!-- 显示闪电数据服务错误,如果有的话 -->
<aura:if isTrue="{!not(empty(v.recordError))}">
    <div class="recordError">
        {!v.recordError}</div>
</aura:if>

</aura:component>

保存记录

LDS的神奇之处在于,在Lightning应用程序中有多个组件可以从相同的记录数据中提取。这些组件中的一部分只是显示记录数据,而其他组件则可以操纵数据本身。该组件加载记录以及一个简短的表单,用户可以在其中输入记录的新名称。

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"
    layoutType="FULL"
    recordId="{!v.recordId}"
    targetError="{!v.recordError}"
    targetRecord="{!v.record}"
    targetFields ="{!v.simpleRecord}"
    mode="EDIT" />

    <!-- 显示有关记录详情的闪电卡 -->
    <div class="Record Details"> 
        <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>
    </div>
    <br/>

    <!-- 显示编辑表格 -->
    <div class="Record Details">
        <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>
    </div>

    <!-- 显示闪电数据服务错误,如果有的话 -->
    <aura:if isTrue="{!not(empty(v.recordError))}">
        <div class="recordError">
            {!v.recordError}</div>
    </aura:if>
</aura:component>
为了处理这个更新,创建一个调用saveRecord()方法的JavaScript控制器。 saveRecord()方法只有一个回调函数SaveRecordResult作为唯一的参数。 SaveRecordResult包括一个状态属性,告诉你保存是否成功,以及其他信息可以用来处理操作的结果。
LdsSaveRecordController.js

({
    handleSaveRecord: function(component, event, helper) {
        component.find("recordEditor").saveRecord($A.getCallback(function(saveResult) {
            if (saveResult.state === "SUCCESS" || saveResult.state === "DRAFT") {
                console.log("保存成功完成");
            } else if (saveResult.state === "INCOMPLETE") {
                console.log("用户离线,设备不支持草稿.");
            } else if (saveResult.state === "ERROR") {
                console.log('问题保存记录,错误:' + 
                           JSON.stringify(saveResult.error));
            } else {
                console.log('Unknown problem, state: ' + saveResult.state + ', error: ' + JSON.stringify(saveResult.error));
            }
        }));}
})
不错,对不对? LDS处理所有幕后的繁重工作,将请求发送到服务器并自动更新这两个记录。

好,现在我们来处理其余的CRUD。

Creating Records

要创建一个空记录,请保持recordId属性有效:recordData undefined。

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"
    layoutType="FULL"
    targetRecord="{!v.newContact}"
    targetFields ="{!v.simpleNewContact}"
    targetError="{!v.newContactError}"
    />

<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>

    <!-- 显示新的联系表格 -->
    <div class="Create Contact">
        <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>
    </div>

    <!-- 显示闪电数据服务错误 -->
    <aura:if isTrue="{!not(empty(v.newContactError))}">
        <div class="recordError">
            {!v.recordError}</div>
    </aura:if>

</aura:component>
在组件控制器中,调用getNewRecord()方法。用户创建记录后,使用上面显示的saveRecord()方法进行保存。
ldsNewRecordController.js

({
    doInit: function(component, event, helper) {
        // 从模板准备一个新的记录
        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);
                }
                else {
                    console.log("Record template initialized: " + rec.sobjectType);
                }
            })
        );
    },

    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") {
                    // 记录已成功保存
                    var resultsToast = $A.get("e.force:showToast");
                    resultsToast.setParams({
                        "title": "Saved",
                        "message": "The record was saved."
                    });
                    resultsToast.fire();

                } else if (saveResult.state === "INCOMPLETE") {
                    // 处理不完整的状态
                    console.log("用户离线,设备不支持草稿");
                } else if (saveResult.state === "ERROR") {
                    // 处理错误状态
                    console.log('问题保存联系,错误: ' + 
                                 JSON.stringify(saveResult.error));
                } else {
                    console.log('未知问题,状态:' + saveResult.state +
                                ', error: ' + JSON.stringify(saveResult.error));
                }
            });
        }
    }
})
这个帮助器验证表单值。
ldsNewRecordHelper.js

({
    validateContactForm: function(component) {
        var validContact = true;

         // 如果必填字段为空,则显示错误消息
        var allValid = component.find('contactField').reduce(function (validFields, inputCmp) {
            inputCmp.showHelpMessageIfInvalid();
            return validFields && inputCmp.get('v.validity').valid;
        }, true);

        if (allValid) {
            // 验证我们有一个帐户附加到
            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);
            
        }  
	}
       
})

删除记录

最后,要删除一条记录,至少指定字段属性设置为“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}"
    />

    <!-- 显示删除记录表格 -->
    <div class="Delete Record">
        <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>
    </div>

    <!-- 显示闪电数据服务错误,如果有的话 -->
    <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("用户离线,设备不支持草稿");
        }
        else if (deleteResult.state === "ERROR") {
            console.log('问题删除记录,错误:' +
                        JSON.stringify(deleteResult.error));
        }
        else {
            console.log('未知问题,状态:' + deleteResult.state +
                        ', error: ' + JSON.stringify(deleteResult.error));
        }
    }));
}})

异步记录保存

假设情况下的时间!因此,您使用的是Salesforce应用程序,由于连接问题,保存尝试无法到达服务器。也许你所乘坐的火车进入了一个隧道,你不小心在建筑物的那个角落里漫不经心地走过,或者那个小房间再次和细胞塔混乱了。无论如何,别担心,LDS会回来的。在发生连接问题时,Lightning Data Service会将您的更改存储在本地缓存中。这由SaveRecordResult对象中的DRAFT状态指示。记录的DRAFT状态在连接恢复时解决。通过LDS保存记录时,本地缓存不会更新,直到保存完成。在服务器上成功完成保存时,缓存将更新为服务器中记录的最新版本,并通知所有引用该记录的组件。保存后,您不必担心手动将记录重新加载到缓存中。 LDS为您处理所有的工作。

如果启用异步保存权限,或者满足以下所有条件,则设备脱机时发生的保存会导致DRAFT状态:

  • 客户端无法访问服务器。
  • 组织已启用脱机草稿。
  • 您拥有9.0或更高版本的Salesforce应用程序。

所有CRUD操作都尝试立即使用XMLHttpRequest解析到服务器。如果您的设备失去与服务器的连接,则Lightning Data Service可以从本地缓存中获取数据。 LDS是否从本地缓存或服务器提取数据取决于记录的年份,或者是否存在本地草案。如果记录足够新,或者存在本地草稿,则LDS将使用本地缓存。 LDS仅在需要时刷新记录,这意味着所有刷新都由组件触发。

创建一个Trailhead游乐场

好消息!您可以在免费的Trailhead Playground(TP)组织中练习使用LDS。什么是TP?这是一个为您定制的Salesforce开发版组织,可用于Trailhead。您可以启动一个TP,或创建一个新的TP,从任何动手挑战。现在就创建一个新的TP(使用现有的组织可能在检查挑战时产生问题)。滚动到此页面的底部。单击“启动”旁边的向下箭头,然后选择创建Trailhead Playground(需要登录)。如果您需要新TP的登录凭据,请按照本文中的说明进行操作。