1. 程式人生 > >Salesforce LWC學習(六) @salesforce & lightning/ui*Api Reference

Salesforce LWC學習(六) @salesforce & lightning/ui*Api Reference

上一篇中我們在demo中使用了很多的 @salesforce 以及 lightning/ui*Api的方法,但是很多沒有細節的展開。其實LWC中針對這些module提供了很多好用的方法,下面對這兩種進行詳細介紹。

一. @Salesforce

@salesforce模組封裝了很多的方法,用於在執行時新增相關的功能。主要方法及使用如下。

1. @salesforce/apex : 此方法用於引入apex的方法,這裡的方法包括自定義的方法以及標準的預置的方法,具體操作如下。
1)引入自定義apex類的方法:我們在專案中和後臺apex程式碼互動,首先需要做的就是使用此標籤引入需要用到的類的方法。

1 import apexMethod from '@salesforce/apex/Namespace.Classname.apexMethod';
2 @wire(apexMethod, { apexMethodParams })
3 propertyOrFunction;

針對此函式相關的變數在上一篇有細節描述,除了引入自定義apex方法以外,還有其他的一些封裝的標準的方法。
2)getSObjectValue(sobject, fieldApiName):此方法用與從一個object中獲取指定的field的值。這個object是從apex中搜索出來的。
此方法兩個引數,sobject代表從後臺apex中搜索出來的資料,fieldApiName為想要查詢欄位值的API name。fieldApiName可以是String型別的api name也可以通過@salesforce/schema方式引入進來。我們在搜尋時可能獲取父層資料,比如搜尋opportunity資料時需要獲取其對應的account的owner資訊,此種父查詢展示的最大深度為5層,比如Opportunity.Account.CreatedBy.LastModifiedBy.Name。 
頭部需要引用:import { getSObjectValue } from '@salesforce/apex';然後呼叫此方法即可。

getSObjectValueDemo.html

1 <template>
2     {accountOwnerName}
3 </template>

getSObjectValueDemo.js:呼叫findOpportunity後臺方法,此方法搜尋了Opportunity的Account.Owner.Name,我們可以使用getSObjectValue獲取其欄位結果。

 1 import { LightningElement, wire } from 'lwc';
 2 import findOpportunity from '@salesforce/apex/OpportunityController.findOpportunity';
 3 import { getSObjectValue } from '@salesforce/apex';
 4 import OPPORTUNITY_ACCOUNT_OWNER_NAME from '@salesforce/schema/Opportunity.Account.Owner.Name';
 5 export default class GetSObjectValueDemo extends LightningElement {
 6 
 7     @wire(findOpportunity,{searchKey:'test'})
 8     opportunityList;
 9 
10     get accountOwnerName() {
11         if(this.opportunityList.data !== undefined) {
12             const opportunity = this.opportunityList.data[0];
13             return getSObjectValue(opportunity,OPPORTUNITY_ACCOUNT_OWNER_NAME);
14         }
15         return '';
16     }
17 }

OpportunityController.cls

 1 public with sharing class OpportunityController {
 2     @AuraEnabled(cacheable=true)
 3     public static List<Opportunity> findOpportunity(String searchKey){
 4         String key = '%' + searchKey + '%';
 5         return [SELECT Id,Account.Owner.Name
 6                     FROM Opportunity
 7                     WHERE Name like :key 
 8                     ORDER BY LastModifiedDate DESC
 9                     limit 1];
10     }
11 }

3)refreshApex(wiredProperty):用於使用wire註解情況下針對快取變數的重新整理,此方法只針對快取變數,其他情況無法使用。

頭部需要引用:import { refreshApex } from '@salesforce/apex';
詳情用法可以檢視上一篇。

4)@salesforce/apexContinuation:主要用於長時間執行的callout,此功能後期會單獨描述。

5)@salesforce/label:用於引入custom label。
頭部需要引入:import labelName from '@salesforce/label/labelReference'
其中:
labelName為我們起的名字,後期js以及前臺html中用到此custom label的地方需要使用此名字作為引用
labelReference為在我們org中宣告的custom label的label name。引入時需要使用namespace.labelName格式進行引用,如果不是manage package,預設的namespace為c。

因為我們在js中可能獲取多個custom label,所以我們可以封裝在一個label的變數中,通過label.labelName方式獲取。custom label我們宣告的時候會有兩種形式,一種是正常的,另外一種是可以使用{0}{1}這種format的操作的label。針對classic以及lightning aura我們可以很輕易的對format的label進行處理,遺憾的是針對lwc並沒有直接的方式去處理,我們可以使用正則表示式進行函式處理。

stringUtils.js:用於解析字串中使用{}處理的佔位符。

1 const formatString = (stringForFormat,params) => {
2     let str = stringForFormat;
3     for (let i = 0; i < params.length; i++) {
4         let re = new RegExp('\\{' + i + '\\}', 'gm');
5         str = str.replace(re, params[i]);
6     }
7     return str;
8 };
9 export {formatString};

getCustomLabelDemo.js:引入方法,針對包含format的字串進行處理。

 1 import { LightningElement } from 'lwc';
 2 import sampleLabel from '@salesforce/label/c.sample_label';
 3 import sampleLabelWithParam from '@salesforce/label/c.sample_label_with_param';
 4 import {formatString} from 'c/stringUtils';
 5 export default class GetCustomLabelDemo extends LightningElement {
 6 
 7     get formatedSampleLabelWithParam() {
 8         let paramList = new Array();
 9         paramList.push('xx');
10         return formatString(sampleLabelWithParam,paramList);
11     }
12 
13     label = {
14         sampleLabel
15     };
16 }

getCustomLabelDemo.html:展示兩個custom label

1 <template>
2     {label.sampleLabel}
3     <br/>
4     {formatedSampleLabelWithParam}
5 </template>

顯示結果:

6)@salesforce/resourceUrl:引入static resource。頭部根據當前的靜態資源是否在managed package中有兩種引用的方式:

1 import resourceName from '@salesforce/resourceUrl/resourceReference';
2 import myResource from '@salesforce/resourceUrl/namespace__resourceReference';

demo如下:

 useStaticResourceDemo.js:引入static resource,在sample目錄下有一個sample.png圖片,前臺展示此圖片

1 import { LightningElement } from 'lwc';
2 import sampleResource from '@salesforce/resourceUrl/lwc_sample';
3 export default class UseStaticResourceDemo extends LightningElement {
4     pictureSampleURL = sampleResource + '/sample/sample.png';
5 }

useStaticResourceDemo.html:展示此圖片

1 <template>
2     <img src={pictureSampleURL}/>
3 </template>

效果展示:

7)@salesforce/schema:引入object以及field的引用。當我們使用LDS或者wire service獲取欄位的引用時,官方建議我們使用此種方式而不是字串方式,詳細使用操作可以檢視上一篇部落格。

8)@salesforce/user:獲取當前user的資訊。通過此引用可以獲取當前user的id 以及當前user是否是一個community guest user。我們通過

import property from '@salesforce/user/property';來引用,property有兩個值:Id / isGuest.下面是一個關於user資訊的獲取的demo。

getUserInfoDemo.js

1 import { LightningElement } from 'lwc';
2 import Id from '@salesforce/user/Id';
3 import isGuest from '@salesforce/user/isGuest';
4 export default class GetUserInfoDemo extends LightningElement {
5     userId = Id;
6     isGuestUser = isGuest;
7 }

getUserInfoDemo.html

1 <template>
2     current user Id : {userId}<br/>
3     current user is Guest {isGuestUser}
4 </template>

以上的內容為@salesforce模組封裝的主要方法,詳情還請自行檢視文件。接下來的幾部分我們講的主要是 lightning/ui*Api部分的wire adapter,其主要是為了擴充LDS標準以外的增強方法。LWC針對資料的操作以及解析如下圖所示。LWC wire adapter以及LDS轉成的都是User Interface API中的內容,所以如果想更好的瞭解各個wire adapter的使用以及返回型別的詳情還請檢視User Interface API。下載地址:https://resources.docs.salesforce.com/220/latest/en-us/sfdc/pdf/api_ui.pdf

二. lightning/uiListApi

此模組主要用於針對一個list view獲取資料以及metadata。我們針對某個物件資料建立列表檢視時,會進行相關的filter,展示某些固定的列,展示多少資料等操作。使用lightning/uiListApi模組我們可以通過object的api name等資訊便可以獲取到指定的list view的資料。需要注意一點的是,此模組以及模組中的方法目前是beta功能。

1. getListUi:此wire adapter方法用於獲取list view中的資料。我們可以通過幾個引數以及幾種形式獲取資料。

1) 使用 object 的api name以及 list view的api name去獲取資料;

1 import { getListUi } from 'lightning/uiListApi';
2 @wire(getListUi, { objectApiName: objName, listViewApiName: listViewName })
3 propertyOrFunction;

其中objName代表想要獲取哪個表的資料,取得是表的api name
listViewName取得是當前表的哪個list view的資料,取得是 list view的api name
2)使用list view的id 獲取資料;

1 import { getListUi } from 'lightning/uiListApi';
2 @wire(getListUi, { listViewId:listViewId})
3 propertyOrFunction;

3)使用object 的api name以及MRU(Most Recently Used)去獲取資料.我們在每個表的list view中都有一個rencent view的檢視,這個是標準的一個檢視,LWC針對此檢視有專門的取法。

1 import { getListUi, MRU } from 'lightning/uiListApi';
2 @wire(getListUi, { objectApiName: objectName, listViewApiName: MRU })
3 propertyOrFunction;

除了上述的必傳的引數以外,此介面卡方法還提供了其他的可選引數,比如pageSize等。

https://developer.salesforce.com/docs/atlas.en-us.uiapi.meta/uiapi/ui_api_resources_list_views_records_md.htm#request_parameters

https://developer.salesforce.com/docs/atlas.en-us.uiapi.meta/uiapi/ui_api_resources_mru_list_views_records.htm

 1 import { LightningElement, wire,track } from 'lwc';
 2 import {getListUi} from 'lightning/uiListApi';
 3 import ACCOUNT_OBJECT from '@salesforce/schema/Account';
 4 export default class GetListUIDemo extends LightningElement {
 5     @track error;
 6     @track accountList;
 7     @wire(getListUi,{objectApiName:ACCOUNT_OBJECT,listViewApiName:'AllAccounts',pageSize:100})
 8     wiredAccountList({error,data}) {
 9         if(data) {
10             this.accountList = data.records.records;
11             this.error = undefined;
12         } else if(error) {
13             this.error = error;
14             this.accountList = undefined;
15         }
16     }
17 }

getListUIDemo.html

 1 <template>
 2     <lightning-card title="get list ui demo" icon-name="standard:action_list_component">
 3  
 4         <table class="slds-table slds-table_cell-buffer slds-table_bordered">
 5             <thead>
 6                 <tr class="slds-line-height_reset">
 7                     <th class="" scope="col">
 8                         <div class="slds-truncate">Account Id</div>
 9                     </th>
10                     <th class="" scope="col">
11                         <div class="slds-truncate">Account Name</div>
12                     </th>
13                     <th class="" scope="col">
14                         <div class="slds-truncate">Type</div>
15                     </th>
16                     <th class="" scope="col">
17                         <div class="slds-truncate">Phone</div>
18                     </th>
19                 </tr>
20             </thead>
21             <tbody>
22                 <template if:true={accountList}>
23                     <template for:each={accountList} for:item="account">
24                         <tr key={account.id}>
25                             <td>{account.id}</td>
26                             <td> {account.fields.Name.value}</td>
27                             <td> {account.fields.Type.value}</td>
28                             <td> {account.fields.Phone.value}</td>
29                         </tr>
30                     </template>
31                 </template>
32                 <template if:false={accountList}>
33                     List View is not contains any data
34                 </template>
35             </tbody>
36         </table>
37     </lightning-card>
38 </template>

結果展示:

 三. lightning/uiObjectInfoApi

此模組的介面卡方法用於獲取object的metadata以及picklist的value。

1. getObjectInfo:使用wire adapter針對指定的object獲取相關的metadata。response包括描述fields/ child relationships / record type/theme metadata資訊。下圖可以看到我們demo中用到的獲取Account的getObjectInfo的結構。我們對於apex中的Schema有了解情況下,可以理解成此介面卡相當於DescribeSObjectResult以及相關的方法用來獲取相關的metadata資訊。demo中我們會演示獲取某個欄位的label資訊,感興趣的可以自行研究。

 

 getSObjectInfoDemo.js:獲取Account表中的AccountSource的label值

 1 import { LightningElement,wire } from 'lwc';
 2 import { getObjectInfo } from 'lightning/uiObjectInfoApi';
 3 import ACCOUNT_OBJECT from '@salesforce/schema/Account';
 4 export default class GetObjectInfoDemo extends LightningElement {
 5     @wire(getObjectInfo, { objectApiName: ACCOUNT_OBJECT })
 6     accountInfo;
 7 
 8     get recordInfo() {
 9         if(this.accountInfo.data !== undefined) {
10             return this.accountInfo.data.fields.AccountSource.label;
11         }
12         return '';
13     }
14 }

getSObjectInfoDemo.html:展示AccountSource的label值

1 <template>
2     {recordInfo}
3 </template>

2. getPicklistValues:使用此wire adapter獲取某個 picklist型別欄位的picklist value。我們接觸apex知道,picklist 欄位獲取可以使用兩種方式,基於record type方式獲取某個record type的picklist values以及獲取picklist型別欄位的所有的values。此wire adapter為通過record type獲取values。此wire adapter需要傳兩個引數,第一個引數是object的某個record type id,第二個引數是想要獲取的picklist型別的欄位API名稱。

@wire(getPicklistValues, { recordTypeId: recordTypeId, fieldApiName: fieldApiName })
propertyOrFunction;

其中,recordTypeId為我們想要操作的指定record type的ID。我們可以根據getObjectInfo 或者getRecordUi這兩個wire adapter獲取。此變數為必填欄位,我們如果想變數改變getPicklistValues動態改變,我們可以使用'$'符號去封裝此變數;fieldApiName為想要查詢欄位的API name,這裡推薦使用@salesforce/schame去獲取field api的reference。返回的型別有兩種,變數或者是方法,變數封裝data和error兩個子變數,使用方法我們可以將這兩個作為引數進行展示。詳見上篇的propertyOrFunction的使用。

下面的demo我們來獲取account中的industry的picklist values。

getObjectInfoDemo.js:首先我們先使用getObjectInfo獲取Account的schema資訊,然後通過getPicklistValues獲取picklist資訊,這裡使用$作為動態傳參,保證當objectInfo變數先裝載,裝載以後執行getPicklistValues。

 1 import { LightningElement,wire} from 'lwc';
 2 import { getObjectInfo } from 'lightning/uiObjectInfoApi';
 3 import ACCOUNT_OBJECT from '@salesforce/schema/Account';
 4 import {getPicklistValues} from 'lightning/uiObjectInfoApi';
 5 import ACCOUNT_INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
 6 export default class GetObjectInfoDemo extends LightningElement {
 7 
 8     @wire(getObjectInfo,{objectApiName:ACCOUNT_OBJECT})
 9     objectInfo;
10 
11     @wire(getPicklistValues,{ recordTypeId: '$objectInfo.data.defaultRecordTypeId', fieldApiName: ACCOUNT_INDUSTRY_FIELD })
12     industryList;
13 }

getObjectInfoDemo.html:使用for:each進行遍歷industry list,使用select option進行展示資料。

 1 <template>
 2     <lightning-card title="Industry List ">
 3         <template if:true={industryList.data}>
 4             <div class="slds-m-around_medium">
 5                 <select>
 6                     <template for:each={industryList.data.values} for:item="item">
 7                         <option key={item.value}>{item.label}</option>
 8                     </template>
 9                 </select>
10             </div>
11         </template>
12     </lightning-card>
13 </template>

效果如下:

 3. getPicklistValuesByRecordType:此方法用於獲取某個物件,某個record type的所有的picklist 的values,這些picklist values封裝在一個map中。

@wire(getPicklistValuesByRecordType, { objectApiName: objectName, recordTypeId: recordTypeId })
propertyOrFunction

其中objectName為想要獲取的object的所有的picklist values的object api name,此欄位必填,推薦使用@salesforce/schema獲取,recordTypeId也是必填欄位,獲取方式同上一個wire adapter。我們通過一個例子更好的理解。

getPicklistValuesByRecordTypeDemo.js:用來獲取record type名稱為 Test Record Type的所有的picklist values資訊。

 1 import { LightningElement, wire,track } from 'lwc';
 2 import {getObjectInfo} from 'lightning/uiObjectInfoApi';
 3 import ACCOUNT_OBJECT from '@salesforce/schema/Account';
 4 import {getPicklistValuesByRecordType} from 'lightning/uiObjectInfoApi';
 5 export default class GetPicklistValuesByRecordTypeDemo extends LightningElement {
 6     @track recordTypeId = '';
 7     @wire(getObjectInfo,{objectApiName:ACCOUNT_OBJECT})
 8     getObjectInfo({data,error}) {
 9         if(data) {
10             const allRecordTypeInfos = data.recordTypeInfos;
11             // eslint-disable-next-line no-console
12             console.log(JSON.stringify(allRecordTypeInfos));
13             this.recordTypeId = Object.keys(allRecordTypeInfos).find(rti => allRecordTypeInfos[rti].name === 'Test Record Type');
14         } else if(error) {
15             //TODO
16         }
17     }
18 
19     @wire(getPicklistValuesByRecordType,{ objectApiName: ACCOUNT_OBJECT, recordTypeId: '$recordTypeId'})
20     picklistValues;
21 
22     get demo() {
23         // eslint-disable-next-line no-console
24         console.log(JSON.stringify(this.picklistValues));
25         return this.recordTypeId;
26     }
27 
28 }

這裡我們使用getObjectInfo獲取所有的record type,打印出來的all record type 資訊如下所示,我們可以看出來,它的結果集是一個Map,key是record type id,value封裝了相關的細節資訊,我們使用Object.key.find來獲取指定名稱的record type id資訊。此使用方法是javascript的遍歷map的使用,感興趣的可以自行檢視。

 

 通過record type id我們使用getPicklistValuesByRecordType獲取所有的picklist valus,格式的層級模式如下圖所示。

 獲取到層級模式以後,我們對程式進行一下增強,獲取所有的AccountSource的picklist values。

在上面的js方法中新增此方法。

1 get accountSourcePicklistValues() {
2    // eslint-disable-next-line no-console
3    console.log(JSON.stringify(this.picklistValues));
4    if(this.picklistValues !== undefined && this.picklistValues.data !== undefined) {
5        return this.picklistValues.data.picklistFieldValues.AccountSource.values;
6     }
7     return '';
8 }

getPicklistValuesByRecordTypeDemo.html

 1 <template>
 2     <lightning-card title="Account Source List ">
 3         <template if:true={accountSourcePicklistValues}>
 4             <div class="slds-m-around_medium">
 5                 <select>
 6                     <template for:each={accountSourcePicklistValues} for:item="item">
 7                         <option key={item.value}>{item.label}</option>
 8                     </template>
 9                 </select>
10             </div>
11         </template>
12     </lightning-card>
13 </template>

效果展示:

三. lightning/uiRecordApi

此模組中的wire adapter主要用於進行資料的CRUD。下面列舉幾個主要的方法,其他的方法還請自行檢視文件。

1. getRecord:用於通過record id獲取此id對應的record 資訊,詳情用法以及引數介紹參看上一篇,有詳細描述。

1 @wire(getRecord, { recordId: string, fields: string[], optionalFields?: string[])
2 propertyOrFunction
3 
4 @wire(getRecord, { recordId: string, layoutTypes: string[],
5                    modes?: string[], optionalFields?: string[])
6 propertyOrFunction

2. getRecordUi:使用此wire adapter可以針對一條或者多條記錄獲取其對應的page layout,metadata以及在UI上面的資料資訊。我們上面也講過如果想要獲取record type id也可以使用此wire adapter。

1 @wire(getRecordUi, { recordIds: string, layoutTypes: string,
2                        modes: string, optionalFields?: string[] })
3 propertyOrFunction;
4 
5   @wire(getRecordUi, { recordIds: string[], layoutTypes: string[], 
6                        modes: string[], optionalFields?: string[] })
7 propertyOrFunction;

可以看到引數和getRecord很相似,返回的物件為Record UI物件以及error兩個。我們可以在User Interface中封裝的Record UI檢視返回的結構以及如何獲取需要的內容資訊。返回的層級結構以及資訊如下圖所示,我們只需要根據不同的取值需要獲取不同的值即可。

3. getFieldValue(record,field):此方法用於獲取記錄中指定field API對應的value。需要注意的是,他獲取的永遠是API value。此方法的demo在上一篇有介紹,可以檢視上一篇。

4. getFieldDisplayValue(record,field):此方法相對上一個方法的區別為此方法獲取的是展示的value。比如一個picklist欄位,我們有國際化操作,針對picklist value可能是英文,但是我們對其中文進行translation,那麼針對語言為中文的客戶,getFieldValue獲取的是picklist 的value,即英文的值,getFieldDisplayValue獲取的是中文的值。

 getFieldDisplayValueDemo.js:此方法用於獲取Account Source的value以及display value

 1 import { LightningElement,api, wire } from 'lwc';
 2 import {getRecord} from 'lightning/uiRecordApi';
 3 import {getFieldValue} from 'lightning/uiRecordApi';
 4 import {getFieldDisplayValue} from 'lightning/uiRecordApi';
 5 import ACCOUNT_ACCOUNT_SOURCE from '@salesforce/schema/Account.AccountSource';
 6 export default class GetFieldDisplayValueDemo extends LightningElement {
 7     @api recordId;
 8 
 9     @wire(getRecord,{recordId:'$recordId',fields:[ACCOUNT_ACCOUNT_SOURCE]})
10     account;
11 
12     get accountSourceValue() {
13         if(this.account.data !== undefined) {
14             return getFieldValue(this.account.data,ACCOUNT_ACCOUNT_SOURCE);
15         }
16         return '';
17     }
18 
19     get accountSourceDisplayValue() {
20         if(this.account.data !== undefined) {
21             return getFieldDisplayValue(this.account.data,ACCOUNT_ACCOUNT_SOURCE);
22         }
23         return '';
24     }
25 }

getFieldDisplayValueDemo.html

1 <template>
2     value : {accountSourceValue}<br/>
3     label : {accountSourceDisplayValue}
4 </template>

效果展示

5. createRecord(recordInput: Record):此方法用於建立一條記錄,其中Record我們需要使用wire service提供的generateRecordInputForCreate(record,objectInfo) 這個wire adapter去例項化一個Record。返回型別為一個Promise,即當前的這個Record,包括當前記錄的page layout中的資訊。使用createRecord以及使用updateRecord這兩個方法前需要先思考是否可以使用lightning-record-form以及lightning-record-edit-form去搞定。搞定不了情況下在使用wire adapter。此wire adapter官方提供了簡單的demo,檢視此連結便可以更好的瞭解此wire adapter:https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.data_salesforce_write

6. updateRecord(recordInput, clientOptions):同上方法,用於編輯一條記錄,引數中的recordInput需要使用wire service提供的generateRecordInputForUpdate(record,objectInfo)去搞定,還是和上面的一樣,如果實在搞定不了在使用此方法,否則儘量別用。

7. deleteRecord(recordId):用於刪除一條記錄,傳入當前的record id即可實現刪除操作。方法為void型別,沒有返回內容。

總結:篇中簡單的整理了一些針對@salesforce 以及 lightning/record*Api模組的方法,細節的方法描述還請自行檢視官方文件以及User Interface API。其實除了這兩個module,LWC還提供了很多好用的module,比如lightning/platformShowToastEvent用於展示toast資訊等等,還要靠小夥伴自己挖掘。另外需要注意的一點的是,針對wire service的這些wire adapter,如果可以使用lightning:record-form相關的搞定,能不用盡量不用,標準功能搞定不了在考慮使用。篇中有錯誤地方歡迎指出,有不懂的歡迎提