本篇可参看:https://trailhead.salesforce.com/modules/lightning_data_service
Lightning中针对object的detail页面,一个lightning app可能包含了多个components,多个components不可避免的会对这个数据进行CRUD操作,如果我们针对每个component都在init操作时后台SQL进行查询,然后赋值给前台变量,进行CUD操作时,还要考虑其他component的数据是否要级联的改变,这种操作以及设计对于性能的影响还是蛮大的,有什么好的方法可以做到一次搜索或者一次加载,所有的components都共用吗?这个时候,LDS或许可以是你想要的。
lightning中,我们使用 Lightning Data Service(LDS)去服务于数据层面,LDS 提供了对数据的访问。
LDS除了可以让一个app的所有的component共用一个share的数据,这样一个更新以后,所有的component(model 为view)都会同步的刷新这个数据。还可以支持用户离线操作数据,当网络连接以后,用户对数据的操作则会进行同步。
LDS优点概括来说:
最小化的XMLHttpRequests
数据只需要搜索一次。
跨 components 分享数据记录
当数据改变以后会创建通知。
优点还有很多,当一个app 涉及到记录的简单的增删改查操作,使用LDS是一个最优的方式。
说了这么多LDS的优点,那LDS如何使用呢?其实只需要在component中引入 <force:recordData>标签即可。
一. <force:recordData>属性介绍 <force:recordData>标签包含以下的常用属性:
recordId : 指定哪条记录来加载,此字段为必须字段。
mode: 指定当前的模式,有 View和Edit两个值。如果针对当前component有update操作,则mode设置为Edit。此字段为必须字段
layoutType: 决定了哪个layout用于加载, FULL/COMPACT。
fields: 决定哪些字段用来搜索出来
layoutType以及fields至少有一个要求必须,因为管理员拥有更改pagelayout的权限,所以layoutType加载的字段具有不确定性,推荐使用fields。
targetRecord: 此属性相当于这条记录ID对应的记录变量,通过此变量可以访问fields中指定的字段值。
targetFields: targetRecord对应的字段的视图,通过此字段可以取出fields中的指定的值。
通过上面的描述可以看出来targetRecord以及targetFields均可以取出fields中指定的字段值,他们两个写法尽管不同,但是他们的表达含义以及取得值相同,后面有具体的描述。
targetError: 此属性存储Error Message, 如果记录没有正确的提供。
recordUpdated:此属性指定当记录进行了CUD操作时的事件处理。
简单Demo如下:其中 {v.accountFields.Name} 等同于{!v.record.fields.Name}.
<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">
<aura:attribute name="record" type="Object"/>
<aura:attribute name="accountException" type="String"/>
<aura:attribute name="accountFields" type="Object"/>
<force:recordData
recordId="{!v.recordId}"
aura:id="accId"
targetRecord="{!v.record}"
targetFields="{!v.accountFields}"
targetError="{!v.accountException}"
fields="Id,Name,Industry"
mode="VIEW"/>
<lightning:card title="{!v.accountFields.Name}">
<lightning:formattedText title="Industry" value="{!v.record.fields.Industry}"/>
</lightning:card>
</aura:component>
二. LDS的 CUD,错误提示以及自带的事件监听操作
force:recordData组件元素自带了好几个方法用来实现数据的简单的增删改操作。controller.js中通过获取到force:recordData元素后调用相关的方法即可进行DML操作。常用方法如下:
通过上面的常用的三个方法,我们会关注到SaveRecordResult这个对象,此类作为CUD操作的callback返回的唯一参数,此对象具有以下的字段:
getNewRecord():此方法用于创建一个空的记录,方法有四个参数:
saveRecord():此方法用于create/edit操作,方法只有一个参数:
deleteRecord():此方法用于delete操作,方法也只有一个参数:
上面说过,通过LDS可以在数据变化后,共用的component实时的刷新数据,但是当两个同样的字段同时在Edit 模式下,则不会同时刷新,即刷新数据仅限于View模式下。
举个例子:Account 新增一个 Action,用来展示和更新Account的部分字段信息。
1). accDisplay.cmp : 用来展示主要的几个字段
1 <aura:component implements="flexipage:availableForRecordHome,force:hasRecordId">
2 <aura:attribute name="record" type="Object"/>
3 <aura:attribute name="accountException" type="String"/>
4 <aura:attribute name="recordFields" type="Object"/>
5 <force:recordData
6 recordId="{!v.recordId}"
7 aura:id="accId"
8 targetRecord="{!v.record}"
9 targetFields="{!v.recordFields}"
10 fields="Name,AccountNumber,Site"
11 mode="VIEW"
12 />
13 <lightning:card title="Acc View" iconName="action:info">
14 <lightning:formattedText title="Name" value="{!v.recordFields.Name}"/>
15 <br/>
16 <lightning:formattedText title="Account Number" value="{!v.recordFields.AccountNumber}"/>
17 <br/>
18 <lightning:formattedText title="Site" value="{!v.recordFields.Site}"/>
19 <br/>
20
21 </lightning:card>
22 </aura:component>
2). accEdit2.cmp:更新固定的几个字段
1 <aura:component implements="force:hasRecordId,
2 force:lightningQuickActionWithoutHeader">
3 <aura:attribute name="record"
4 type="Object" description="current record reference"/>
5 <aura:attribute name="recordFields"
6 type="Object" description="current record fields list"/>
7 <aura:attribute name="recordError"
8 type="String" description="if error occurs,record error reference"/>
9
10 <force:recordData aura:id="editRecordId"
11 fields="Name,AccountNumber,Site"
12 recordId="{!v.recordId}"
13 targetError="{!v.recordError}"
14 targetRecord="{!v.record}"
15 targetFields="{!v.recordFields}"
16 mode="EDIT"
17 />
18
19 <!-- Display an editing form -->
20 <div class="Edit Account Details">
21 <lightning:card iconName="action:edit" title="{!v.recordFields.Name}">
22 <div class="slds-p-horizontal--small">
23
24 <lightning:input label="Name"
25 value="{!v.recordFields.Name}"/>
26 <br/>
27 <lightning:input label="Account Number"
28 value="{!v.recordFields.AccountNumber}"/>
29 <br/>
30 <lightning:input label="Site"
31 value="{!v.recordFields.Site}"/>
32 <br/>
33
34 <lightning:button label="Save"
35 variant="brand"
36 onclick="{!c.handleSaveRecord}"/>
37 </div>
38 </lightning:card>
39 </div>
40
41 <!-- if error occurs, display operate error -->
42 <aura:if isTrue="{!not(empty(v.recordError))}">
43 <div class="recordError">
44 {!v.recordError}
45 </div>
46 </aura:if>
47 </aura:component>
3). AccComponent.cmp: 通过Tab方式展示数据信息,因为想要测试一个场景,所以此component里面引用了两遍accEdit2.cmp
1 <aura:component implements="flexipage:availableForAllPageTypes,force:lightningQuickAction,force:hasRecordId">
2 <lightning:tabSet class="slds-tabs_scoped">
3 <lightning:tab label="Acc View">
4 <c:AccDisplay recordId="{!v.recordId}"/>
5 </lightning:tab>
6 <lightning:tab label="Acc Edit">
7 <c:AccEdit2 recordId="{!v.recordId}"/>
8 </lightning:tab>
9 <lightning:tab label="Acc Edit2">
10 <c:AccEdit2 recordId="{!v.recordId}"/>
11 </lightning:tab>
12 </lightning:tabSet>
13 </aura:component>
4). 新建一个Lightning Action,并且将此Action放在Page Layout中
结果展示:
1.点击Edit Test会弹出Pop Up Window
2.在ACC EDIT Tab输入了内容点击Save以后,结果展示位ACC View的Tab会立刻显示输入后save的内容,而ACC Edit2 Tab则会保留原值。
通过结果可以看出来,在mode为view情况下,save以后会立刻显示在view视图中。在mode为Edit情况下,如果edit1有某个字段更新,比如Account Number,则edit2的Account Number不会被更新。这种设计也是正常的,因为如果同步更新会有歧义,但是我们有一些场景还是希望Edit模式下也可以自动更新这些值得,这个时候就可以使用到自带的事件监听。
自带事件监听使用非常简单,只需要<force:recordData>标签增加属性:recordUpdated属性设置你需要后台controller绑定的handler即可。
使用LDS的事件监听有4中类型: CHANGED / LOADED / REMOVED / ERROR。 从名字可以看出来分别对应着 更改 / 加载 / 移除 / 错误。
下面我们针对上面的程序进行改进,对accEdit2.cmp 优化一下force:recordData组件元素
1 <force:recordData aura:id="editRecord2Id"
2 fields="Name,AccountNumber,Site"
3 recordId="{!v.recordId}"
4 targetError="{!v.recordError}"
5 targetRecord="{!v.record}"
6 targetFields="{!v.recordFields}"
7 mode="EDIT"
8 recordUpdated="{!c.reloadUpdate}"
9 />
对accEdit2Controller.js增加一个reloadUpdate方法,其中调用reloadRecord()方法可以对force:recordData进行重新数据加载。
1 reloadUpdate : function(component, event, helper) {
2 var eventParams = event.getParams();
3 if(eventParams.changeType == "CHANGED") {
4 // get the changed fields for this record
5 var changedFields = eventParams.changedFields;
6 console.log('Chaged Fields: ' + JSON.stringify(changedFields));
7 component.find('editRecordId').reloadRecord();
8 } else if(eventParams.changeType === "LOADED") {
9 console.log("Record is loaded successfully.");
10 } else if(eventParams.changeType === "REMOVED") {
11 var deleteRecordResult = $A.get("e.force:showToast");
12 deleteRecordResult.setParams({
13 "title": "Delete Result",
14 "message": "The record was deleted."
15 });
16 deleteRecordResult.fire();
17 } else if(eventParams.changeType === "ERROR") {
18 console.log('Error: ' + component.get("v.error"));
19 }
20 }
增强上述方法以后,在重新运行,当ACC Edit Tab中改了相关的值以后,ACC Edit2 Tab的值也会同步的更新,因为LDS已经加载成了最新的值。
(注意:调用reloadRecord()方法以后,会重新执行事件类型为LOADED的事件设置,demo中如果save以后会打出Record is loaded successfully.)
总结:使用LDS可以在不使用controller情况下便进行简单的CUD操作,很类似classic 中的standcontroller功能。这种方式在lightning中还是很常见的,但是如果涉及到复杂的数据关联的改动或者transaction中需要进行多次更新操作,建议不使用LDS换成后台controller中去做。篇中只弄了Edit的demo,new以及delete的demo感兴趣的可以自己去玩。篇中有错误的欢迎指出,有问题欢迎留言。