本篇可參看: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操作。常用方法如下:
- getNewRecord():建立一條新記錄,通常用于insert操作,init handler中調用此方法建立一條ID為空的記錄,此方法不會傳回任何類型的callback function;
- saveRecord(): 用于 insert/update記錄,此方法會傳回一個 SaveRecordResult對象的callback function,SaveResult後面會詳細介紹;
- deleteRecord():用于記錄的delete操作,此方法會傳回一個SaveRecordResult對象的callback function。
通過上面的常用的三個方法,我們會關注到SaveRecordResult這個對象,此類作為CUD操作的callback傳回的唯一參數,此對象具有以下的字段:
- objectApiName:目前操作的object的 API name;
- entityLabel: 目前操作的object的label name;
- error: 如果發生系統或者自定義的操作此記錄的錯誤,則傳回在error字段裡面,此error字段傳回的類型為list,可以通過for循環疊代出所有的錯誤資訊。如果目前的操作狀态為操作成功(save state為success或者draft)則error為undefined;
- recordId: 目前操作記錄的18位的ID;
- state: 傳回目前操作的結果狀态: SUCCESS(操作成功)/ DRAFT(Server不可達,本地存儲成功)/ INCOMPLETE(Server不可達,本地不支援存儲)/ERROR(存儲錯誤,由于validation或者其他的原因)。
getNewRecord():此方法用于建立一個空的記錄,方法有四個參數:
- objectApiName: 需要建立的 object 的API name;
- recordTypeId: 需要建立的object的record type 的ID,如果沒有指定,預設為default record type;
- skipCache:判斷是否從server端擷取object的template還是從用戶端擷取;
- callback:當object建立完以後的回調函數,此函數沒有任何參數,建立完成以後會自動調用此方法。
saveRecord():此方法用于create/edit操作,方法隻有一個參數:
- callback:當object create/update完成以後的回調函數,次函數也包含了一個參數,即上面提到的SaveResultResult對象的變量引用。
deleteRecord():此方法用于delete操作,方法也隻有一個參數:
- callback:當object delete完成以後的回調函數,次函數也包含了一個參數,即上面提到的SaveResultResult對象的變量引用。
上面說過,通過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中
![](https://img.laitimes.com/img/9ZDMuAjOiMmIsIjOiQnIsIyZuBnL5MzMwEzMzUTNtMDOwMTM2UzNxEzM4ADOxAjMtYjN5ATM58CX4ADOxAjMvwlN2kDMxkzLcd2bsJ2Lc12bj5ycn9Gbi52YugTMwIzcldWYtl2Lc9CX6MHc0RHaiojIsJye.png)
結果展示:
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感興趣的可以自己去玩。篇中有錯誤的歡迎指出,有問題歡迎留言。
作者:zero
部落格位址:http://www.cnblogs.com/zero-zyq/
本文歡迎轉載,但未經作者同意必須保留此段聲明,且在文章頁面明顯位置給出原文連接配接
個人下載下傳了一些相關學習的PDF檔案,如果需要下載下傳請通路百度雲 點選此處通路 密碼:jhuy
如果文章的内容對你有幫助,歡迎點贊~
為友善手機端檢視部落格,現正在将部落格遷移至微信公衆号:Salesforce零基礎學習,歡迎各位關注。