Salesforce LWC學習(五) LDS & Wire Service 實現和後臺數據交互 & meta xml配置

以前的幾節都是基於前臺變量進行相關的操做和學習,咱們在項目中不可避免的須要獲取數據以及進行DML操做。以前的內容中也有提到wire註解,今天就詳細的介紹一下對數據進行查詢以及DML操做以及Wire Service相關的知識。javascript

一. LDShtml

學習LDS之前能夠先看一下aura中LDS的概念salesforce lightning零基礎學習(六)Lightning Data Service(LDS)。針對LWC中的LDS和aura中的功能原理很像,區別多是語法和標籤的區別。因此這裏對LDS不作過多的描述,直接展開標籤的用法。前端

LWC 封裝了3個最基礎的組件去和數據進行交互。分別是lightning-record-form / lightning-record-edit-form / lightning-record-view-form。和aura的用法也相似,lightning-record-form能夠做爲view/edit視圖使用,lightning-record-edit-form針對edit視圖使用並能夠進行最大可能的自定義UI,lightning-record-view-form針對view視圖使用並能夠進行最大可能的自定義UI。java

他們能夠實現最基礎的交互,若是他們標準功能知足不了,咱們須要更加的自定義的功能,須要使用@wire 去指定LDS 的wire adapter。(封裝在lightning/ui*Api中)react

1. lightning-record-form編程

當咱們只有ID,但願根據當前的用戶顯示當前用戶對應的page layout佈局的內容。咱們即可以使用 lightning-record-form標籤了,此標籤遵循着FLS關係,用戶只能看到本身可見的字段。此標籤有三個模式:
view: 以output field展現,針對有權限編輯的字段,會顯示編輯的按鈕,當編輯某個值之後會顯示save/cancel 按鈕。api

read-only:和上面區別爲不顯示可編輯按鈕。promise

edit:以輸入框進行展現,而後顯示save/cancel按鈕。緩存

myComponentWithRecord 展現了 lightning:record-form的view的demo。app

myComponentWithRecord.html:使用lightning-record-form展現account的detail數據,layout-type選擇的是compact,mode爲view。

1 <template>
2     <lightning-record-form
3         record-id={recordId}
4         object-api-name="Account"
5         layout-type="Compact"
6         mode="view">
7     </lightning-record-form>
8 </template> 

myComponentWithRecord.js:聲明一個public的變量,名稱固定爲recordId。

1 // myComponent.js
2 import { LightningElement, api } from 'lwc';
3 export default class MyComponent extends LightningElement {
4     @api recordId;
5 }

myComponentWithRecord.js-meta.xml:配置當前component只容許用到record page中,而且只有account的record page能夠選擇此component。下面的內容咱們會詳細的講解如何配置此xml文件,以及各個標籤的做用。

 1 <?xml version="1.0" encoding="UTF-8"?>
 2 <LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata" fqn="myComponentWithRecord">
 3     <apiVersion>46.0</apiVersion>
 4     <isExposed>true</isExposed>
 5     <targets>
 6         <target>lightning__RecordPage</target>
 7     </targets>
 8     <targetConfigs>
 9         <targetConfig targets="lightning__RecordPage">
10             <property name="recordId" type="String"></property>
11             <objects>
12                 <object>Account</object>
13             </objects>
14         </targetConfig>
15     </targetConfigs>
16 </LightningComponentBundle>

配置完之後咱們只須要找到一個account,更改page layout或者app builder中針對account的lightning record page拖拽此component便可顯示。

上面的demo中,咱們在lightning-record-form中聲明瞭一些簡單的屬性,除了上述的屬性之外,此標籤還有不少可選擇的屬性。全部屬性以下:

  • record-type-id: record type的ID,此屬性用於當前的object有多個record type而且咱們不想建立default的record type狀況,這時咱們須要傳遞 record type id;
  • mode: view/edit/readonly。當咱們沒有指定ID的狀況下,則這個類型默認是edit,即要建立一個object的記錄;當有id狀況下,默認是view。
  • layout-type: Compact / Full. 用來指定當前的layout展示的形式。當咱們新建記錄時,即record id爲空的狀況下,layout-type只能渲染成Full.
  • record-id: 須要展現/操做的記錄ID,若是此屬性爲空,則表明要新建一條記錄;
  • object-api-name: 當前想要操做的object的API name,此屬性是必填屬性
  • columns: 表單中的列數,一般lightning:record-form不須要設置;
  • fields: 若是咱們不想經過layout-type展現,咱們能夠設置此fields選項,去按照咱們的要求顯示指定的字段信息。固然lightning:record-form不建議使用此屬性,若是想要自定義顯示字段,咱們能夠考慮用lightning:record-view-form以及lightning:record-edit-form去實現read/edit模式。
  • density:設置label以及field在表單中的排列樣式。有三個值: compact / comfy / auto.其中auto是default的值。

除了上述的屬性之外,由於lightning-record-form有edit mode,因此它還擁有一些方法(如下僅用於edit mode,readonly不可用):

  • load:當LDS加載數據完成後會調用此事件,此事件有一個返回的參數是detail,咱們能夠經過event.detail獲取相關的內容;
  • submit:當form表單提交了改變了的data時會自動觸發此事件,此事件有一個可傳入的參數fields,此參數用來指定要操做的字段集合;
  • success:當form表單提交執行成功之後會自動觸發此事件,此事件有一個返回的參數是detail;
  • error:當form表單提交執行失敗之後會自動觸發此事件,返回的參數有detail;
  • cancel:當form表單沒有提交點擊cancel之後會自動觸發此事件。

這幾個事件在某種狀況下仍是有必定聯繫的。

  • 當咱們執行submit事件之後,在沒有錯誤的狀況下,會先執行load事件,執行成功之後會執行success事件,當執行完success事件之後會再一次load事件。 submit -> load -> success -> load。
  • 當咱們執行submit事件之後,若是有錯誤會執行error事件。 submit -> error。
  • 當咱們執行cancel事件之後,將會執行load事件。cancel -> load。
  • 當咱們執行完cancel事件之後,頁面的cancel/submit按鈕會隱藏,可編輯字段會展現編輯的圖標,當咱們對某個字段進行編輯時,會執行load事件。

下面經過一個demo更好的瞭解edit功能。

myComponentWithRecordEdit.html:展現一個edit模式的lightning-record-form,並針對這些標準的事件設置相關的handler。

 1 <template>
 2     <lightning-record-form
 3         record-id={recordId}
 4         object-api-name={objectApiName}
 5         fields={fields}
 6         columns="2"
 7         mode="edit"
 8         onsubmit={handleSubmit}
 9         onsuccess={handleSuccess}
10         onerror={handleError}
11         oncancel={handleCancel}
12         onload={handleLoad}
13         >
14     </lightning-record-form>
15 </template>

myComponentWithRecordEdit.js:設置相關的handler邏輯,頭部咱們能夠看到import salesforce/lightning相關的內容。這個咱們在後續會以refrence內容詳細說明。這裏還有event.preventDefault()方法。當咱們捕獲submit 事件並以編程方式提交表單,這種狀況咱們須要使用event.preventDefault方法去取消事件的默認行爲,不然會進行重複的表單提交。

 1 /* eslint-disable no-console */
 2 import { LightningElement, api } from 'lwc';
 3 import { ShowToastEvent } from 'lightning/platformShowToastEvent';
 4 
 5 import NAME_FIELD from '@salesforce/schema/Account.Name';
 6 import REVENUE_FIELD from '@salesforce/schema/Account.AnnualRevenue';
 7 import INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
 8 
 9 export default class MyComponentWithRecordEdit extends LightningElement {
10     // The record page provides recordId and objectApiName
11     @api recordId;
12     @api objectApiName;
13 
14     fields = [NAME_FIELD, REVENUE_FIELD, INDUSTRY_FIELD];
15 
16     handleLoad(event) {
17         console.log('execute handle load');
18     }
19 
20     handleSubmit(event){
21         console.log('execute handle submit');
22         event.preventDefault();       // stop the form from submitting
23         const fields = event.detail.fields;
24         fields.LastName = 'My Custom Last Name'; // modify a field
25         this.template.querySelector('lightning-record-form').submit(fields);
26     }
27 
28     handleSuccess(event) {
29         console.log('execute handle success');
30         const evt = new ShowToastEvent({
31             title: "Account Operated",
32             message: "Record ID: " + event.detail.id,
33             variant: "success"
34         });
35         this.dispatchEvent(evt);
36     }
37 
38     handleError(event) {
39         console.log('execute handle error');
40         const evt = new ShowToastEvent({
41             title: "Account Operated",
42             message: event.detail.message,
43             variant: "error"
44         });
45         this.dispatchEvent(evt);
46     }
47 
48     handleCancel(event) {
49         console.log('execute handle cancel')
50         const evt = new ShowToastEvent({
51             title: "Account canceled",
52             variant: "cancel"
53         });
54         this.dispatchEvent(evt);
55     }
56 }

效果展現:

1) 點擊Save更新數據操做

2) 當Save後有error狀況

 2. lightning-record-view-form

lightning-record-form功能確實比較好用,可是若是用戶想要顯示指定的字段而且但願字段以指定的順序進行顯示只讀的pagelayout時,使用lightning-record-form便沒法實現了,這個時候咱們須要使用lightning-record-view-form搭配lightning-output-field用來實現按照本身的要求展現一個或者多個字段。顯示時,咱們一般搭配grid一塊兒使用按需展示多行多列效果。grid使用能夠參考:https://www.lightningdesignsystem.com/utilities/grid/

 此組件有如下的屬性可供選擇:

  • record-id:當前要顯示記錄的記錄ID,此字段必填;
  • object-api-name: 當前object的API 名稱,此字段必填;
  • density:設置label以及field在表單中的排列樣式。

除上述屬性之外,lightning-record-view-form支持load事件,可用參數爲data,存儲的是記錄的數據。詳見上面的demo。下面的demo爲使用此標籤實現只讀的數據。

 myComponentWithRecordView.html:經過引入lightning-record-view-form,而後配合lightning-output-field展現信息,這裏展現的是一行四列的內容佈局。

 1 <template>
 2     <lightning-record-view-form
 3                 record-id={recordId}
 4                 object-api-name="Account">
 5         <div class="slds-grid">
 6             <div class="slds-col">
 7                 <lightning-output-field field-name="Name"></lightning-output-field>
 8             </div>
 9             <div class="slds-col">
10                 <lightning-output-field field-name="Phone"></lightning-output-field>
11             </div>
12             <div class="slds-col">
13                 <lightning-output-field field-name="AnnualRevenue"></lightning-output-field>
14             </div>
15             <div class="slds-col">
16                 <lightning-output-field field-name="Industry"></lightning-output-field>
17                 
18             </div>
19         </div>
20     </lightning-record-view-form>
21 </template>

myComponentWithRecordView.js

1 import { LightningElement, api } from 'lwc';
2 
3 export default class MyComponentWithRecordView extends LightningElement {
4     @api recordId;
5 }

顯示效果:

 3. lightning-record-edit-form

lightning-record-form能夠實現create/edit功能,和view的狀況同樣,當用戶想要深度自定義時,lightning-record-form顯然達不到需求,這個時候咱們就須要 lightning-record-edit-form。
此組件一般和lightning-input-field一塊兒用,用來顯示須要編輯的字段。咱們針對佈局中偶爾可能須要顯示只讀字段,咱們可使用lightning-output-field以及lightning-formatted-name一塊兒搭配使用。
此組件支持的方法和lightning-record-form基於edit模式下差不太多,同lightning-record-form同樣,若是想要建立記錄,只須要record-id爲空便可。

lightning-record-form與lightning-record-edit-form使用不一樣的地方能夠整理兩點:

1) lightning-record-form有cancel事件,lightning-record-edit-form沒有,須要使用lightning-button展現。針對cancel按鈕的默認處理方式也不一樣,針對lightning-record-form點擊cancel之後會默認恢復view的狀態,針對lightning-record-edit-form不能夠,咱們須要針對字段調用reset方法才能夠;
2) lightning-record-edit-form 須要使用lightning-button而且type爲submit才能夠正常提交表單,lightning-record-form不須要。

 下面經過一個demo進行更好的瞭解。

recordEditFormSample.html

 1 <template>
 2     <lightning-record-edit-form
 3         record-id={recordId}
 4         object-api-name={objectApiName}
 5         onsubmit={handleSubmit}
 6         onload={handleLoad}
 7         onsuccess={handleSuccess}
 8         onerror={handleError}
 9         >
10         <lightning-messages></lightning-messages>
11         <lightning-input-field field-name="Name"></lightning-input-field>
12         <lightning-input-field field-name="Industry"></lightning-input-field>
13         <lightning-input-field field-name="AnnualRevenue"></lightning-input-field>
14         <div class="slds-m-top_medium">
15             <lightning-button class="slds-m-top_small" label="Cancel" onclick={handleReset}></lightning-button>
16             <lightning-button class="slds-m-top_small" type="submit" label="Save Record"></lightning-button>
17         </div>
18     </lightning-record-edit-form>
19 </template>

recordEditFormSample.js

 1 /* eslint-disable no-console */
 2 import { LightningElement, api } from 'lwc';
 3 import { ShowToastEvent } from 'lightning/platformShowToastEvent';
 4 export default class RecordEditFormSample extends LightningElement {
 5 
 6     @api recordId;
 7     @api objectApiName;
 8 
 9     handleSubmit(event) {
10         event.preventDefault();       // stop the form from submitting
11         const fields = event.detail.fields;
12         if(fields.Industry === null || fields.Industry === '') {
13             const evt = new ShowToastEvent({
14                 title: "Account Operated Failed",
15                 message: "Account Industry cannot be blank",
16                 variant: "error"
17             });
18             this.dispatchEvent(evt);
19             return;
20         }
21         this.template.querySelector('lightning-record-edit-form').submit(fields);
22     }
23 
24     handleLoad(event) {
25         console.log('execute load');
26     }
27 
28     handleSuccess(event) {
29         const evt = new ShowToastEvent({
30             title: "Account Operated Success",
31             message: "Record is :" + event.detail.id,
32             variant: "success"
33         });
34         this.dispatchEvent(evt);
35     }
36 
37     handleError(event) {
38         const evt = new ShowToastEvent({
39             title: "Account Operated Failed",
40             message: event.detail.message,
41             variant: "error"
42         });
43         this.dispatchEvent(evt);
44     }
45 
46     handleReset(event) {
47         const inputFields = this.template.querySelectorAll(
48             'lightning-input-field'
49         );
50         if (inputFields) {
51             inputFields.forEach(field => {
52                 field.reset();
53             });
54         }
55      }
56 }

效果展現

1) 包含錯誤的狀況展現

2. 正常保存的展現

 二. Wire Service

從上面內容能夠看到,LDS已經很強大了,可是針對LDS處理不了的狀況呢,好比獲取父表信息,對數據中的內容進行格式化處理,這些可能標準功能很難達到或者達不到,這種咱們便須要wire adapter去對LDS進行加強。

1. 使用封裝的函數進行LDS加強

咱們在組件中使用@wire標籤在javascript中去獲取數據,這些數據由lightning/ui*Api 模塊的一個wire adapter獲取。

wire adapter有不少強大的功能,好比getRecord / getFieldValue(record, field)。 咱們在代碼中常常會看到 import salesforce/xxx 以及 import lightning/ui*Api/xxx,咱們會在下一節LWC博客中詳細的講解。

咱們稱wire service在某種程度上是reactive的,緣由是它提供了一個reactive的變量,咱們使用$符號聲明在變量前面,當這個變量改變之後,wire service將會獲取一個新的版本的數據,從而從新渲染component。

咱們基於三個步驟使用wire service。

1 import { adapterId } from 'adapterModule';
2 @wire(adapterId, adapterConfig)
3 propertyOrFunction;

adapterId: wire adapter的標識符。好比咱們須要用到lightning/uiRecordApi中的getRecord,那getRecord爲這裏的adapterId;
adapterModule:當前這個標識符封裝在哪一個適配器模塊中,lwc封裝了好多的wire adapter 標識符,他們在如下的adapterModule中:lightning/uiListApi / lightning:uiObjectInfoApi / lightning:uiRecordApi
adapterConfig:針對wire adapter特定的配置對象信息。配置對象的屬性值能夠是字符串,也能夠經過@salesforce/schema方式引入的表和字段信息。salesforce比較推薦後者;
propertyOrFunction:一個私有變量或者方法,這個變量或者方法從wire service經過配置信息獲取到最終的數據。若是這個是一個變量聲明瞭wire,返回的結果爲data property以及error property,若是這個是一個方法聲明瞭wire,這個方法返回的結果包含data property 以及error property。

概念看起來比較繞,經過一個demo(getRecord)即可以更好的瞭解。
https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.reference_wire_adapters_record

getRecord wire adapter在lightning/uiRecordApi模塊下,因此第一個步驟肯定爲:
import {getRecord} from 'lightning/uiRecordApi'

adapterConfig 針對getRecord有兩個能夠的配置項。

1) { recordId: string, fields: string[], optionalFields?: string[]}

2){ recordId: string, layoutTypes: string[], modes?: string[], optionalFields?: string[]}

  • recordId表明想要獲取的記錄的ID
  • fields / layoutTypes這兩個必需要有一個存在。fields表明當前想要查詢的字段,官方建議咱們使用@salesforce/schema方式獲取相關的metadata信息。這裏須要注意的是,若是當前的上下文用戶沒有針對字段的訪問權限,將會報錯。
  • layoutTypes: compact/full這兩個取值,由於pagelayout可能會有改變,因此針對要求固定字段的狀況下,使用上述fields方式。若是要跟隨page layout方式,能夠選擇此方式
  • modes: 當選擇了layoutTypes之後,咱們能夠選擇modes,可選擇的值爲Create/Edit/View。
  • optionalFields和fields功能相似,區別爲若是引入一個上下文用戶沒有訪問權限的字段,使用此參數不會報錯,沒有權限的字段對應的值不會返回而已。

demo用於獲取account的name並對name進行每一個字母都大寫處理:

 RecordViewFormWithWireService.js:使用wire adapter中的getRecord獲取相關metadata的value,而後進行format處理。salesforce建議咱們獲取metadata命名採用object_field_name方式,固然這個是規範,不是規則。這裏之因此對wiredAccount獲取值部分添加data不爲undefined緣由是當咱們加載數據時,最開始wiredAccount爲{},因此get accountName方法會掛掉提示undefined信息,取Account Name值有兩種方式,一種是經過各類點的方式取到,另外一個是經過wire service封裝的getFieldValue方法獲取。

 1 /* eslint-disable no-console */
 2 import { LightningElement,wire,api,track } from 'lwc';
 3 import { getRecord,getFieldValue } from 'lightning/uiRecordApi';
 4 import ACCOUNT_NAME_FIELD from '@salesforce/schema/Account.Name';
 5 import ACCOUNT_INDUSTRY_FIELD from '@salesforce/schema/Account.Industry';
 6 import ACCOUNT_ANNUAL_REVENUE from '@salesforce/schema/Account.AnnualRevenue';
 7 const ACCOUNT_FIELDS = [ACCOUNT_NAME_FIELD,ACCOUNT_INDUSTRY_FIELD,ACCOUNT_ANNUAL_REVENUE];
 8 export default class RecordViewFormWithWireService extends LightningElement {
 9     @api recordId;
10 
11     @wire(getRecord,{recordId:'$recordId',fields:ACCOUNT_FIELDS})
12     wiredAccount;
13 
14     get accountName() {
15         // console.log(JSON.stringify(this.wiredAccount));
16         // console.log('xx');
17         // if(this.wiredAccount.data !== undefined) {
18         //     return this.wiredAccount.data.fields.Name.value.toUpperCase();
19         // }
20         // return '';
21         return this.wiredAccount.data != undefined ? getFieldValue(this.wiredAccount.data,ACCOUNT_NAME_FIELD).toUpperCase() : '';
22     }
23 }

  recordViewFormWithWireService.html

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

結果展現:

上面的wire service中只簡單的描述了getRecord的用法以及順帶描述了getFieldValue,LWC提供了不少的wire adapter function,下一節的博客會有詳細的說明。當咱們使用了wire adapter加強LDS之後,能夠作到更強的功能,好比獲取父對象字段值,進行字段值的format。可是咱們想要更復雜的操做,好比對數據進行filter,獲取子數據信息,那咱們就得須要訪問apex獲取數據了。下面內容爲經過apex獲取數據。

2. 和後臺apex方法交互

有兩種方式能夠調用apex方法,一種是wire方式直接調用,另一種經過指定的命令方式。下面對這兩種方式進行簡單的介紹。

兩種方式的第一步均須要引入這個須要調用的apex

1 import apexMethodName from '@salesforce/apex/Namespace.Classname.apexMethodReference';

apexMethodName—用來識別引入的apex方法的名稱。這個名稱一般和方法名稱相同而且後期引用均使用此名稱。
apexMethodReference—引用的apex中的method的名稱
Classname—當前的method所在的apex class的名稱
Namespace—當前的namespace,默認爲c.若是是c的狀況下能夠自動省略

好比咱們在 ContactController中有一個方法名字是getContactList,則咱們的apexMethodName默認應該命名爲 getContactList,
apexMethodRefernce爲getContactList,Classname爲ContactController,Namespace爲c。以下所示:
import getContactList from '@salesforce/apex/ContactController.getContactList';

咱們在aura項目中,若是js中調用apex中的方法要求當前的方法聲明爲@AuraEnabled,一樣使用LWC也要求後臺的apex方法須要聲明爲@AuraEnabled,而且方法要求static & (public / global)
當咱們針對的是獲取數據,沒有DML操做狀況下,咱們能夠聲明方法爲cacheable=true去提高客戶端的性能。若是當前的數據存在DML操做,不是純粹的取數據,則不該該聲明cacheable=true。

咱們針對和apex交互的兩種方式,使用wire方式必需要指定後臺的apex方法聲明 cacheable=true,使用命令方式則不須要有這個限制。固然,若是咱們使用了cacheable聲明之後,當咱們以爲數據可能不是最新或者是有問題的數據狀況下,咱們能夠調用refreshApex()去獲取最新的數據。

若是咱們apex中涉及到和外部系統的長時間的交互,咱們能夠對方法聲明 continuation=true,若是同時聲明瞭 cacheable以及continuation,則中間使用空格分隔。以下所示:
@AuraEnabled(continuation=true cacheable=true)

後臺方法的要求已經說完了,下面介紹兩種方式的調用。

1)wire方式調用:

@wire(apexMethod, { apexMethodParams })
propertyOrFunction;

apexMethod: 和上面import的apexMethodName相同;
apexMethodParams:apexMethodName傳遞的參數。後臺的方法能夠無參數和有參數,若是無參數將apexMethodParams設置爲null,若是有參數則傳遞此參數。這裏須要注意的是,若是apexMethodParams設置爲null能夠正常調用,意思是無參方法,若是此參數爲undefined,則wire不會調用後臺的此方法。
propertyOrFunction:wire裝載給變量或者方法。若是是變量,後臺方法若是沒有錯誤狀況下,返回的是正常的返回內容。不然返回的是error變量。
若是是方法,則方法對應的是一個object,object中包含了data變量或者error變量。提及來比較繞,經過一個例子更好的瞭解。

下面的例子爲wire裝載給方法。wiredContacts返回變量中有兩個參數,error,data。咱們一般判斷若是data不爲空,則正常返回。若是error不爲空,則表明當前的數據獲取存在異常。demo中沒有形參,若是想要有形參,在getContactList方法後面使用逗號分隔之後添加形參

 1 @wire(getContactList)
 2 wiredContacts({ error, data }) {
 3     if (data) {
 4         this.contacts = data;
 5         this.error = undefined;
 6     } else if (error) {
 7         this.error = error;
 8         this.contacts = undefined;
 9     }
10 }

下面的例子爲wire裝載給變量。findContacts有一個參數searchKey。咱們使用$符號表明當前的變量是動態的reactive的,返回值給contacts。若是正常返回,contacts裏面是後臺的apex 返回的數據列表。若是存在error,contacts裏面是error變量。

1 @wire(findContacts, { searchKey: '$searchKey' })
2 contacts;

由於後臺聲明瞭cacheable之後,只有刷新之後才能從新裝載最新版本的數據。LWC針對wire聲明的變量提供了refreshApex方法。使用兩步走。

1. import { refreshApex } from '@salesforce/apex';

2. refreshApex(wiredProperty)

其中wiredProperty爲咱們使用wire標籤聲明的變量。這裏須要注意的一點是,針對wire聲明的方法沒法使用此方法進行刷新緩存操做。
若是聲明瞭方法咱們想清空緩存,須要先聲明變量。而後方法中對此變量賦值,而後再refreshApex中傳遞聲明的變量。

下面以一個例子更好的瞭解wire方式調用apex代碼以及refreshApex的使用。

ContactController中聲明瞭一個方法findContacts,形參是一個string用來傳遞想要搜索的contact的name。此方法使用AuraEnabled而且指定了cacheable=true,則LWC針對前臺處理可使用wire方式,也可使用命令方式。

1 public with sharing class ContactController {
2     @AuraEnabled(cacheable=true)
3     public static List<Contact> findContacts(String searchKey) {
4         String key = '%' + searchKey + '%';
5         return [SELECT Id, Name, Title, Phone, Email FROM Contact WHERE Name LIKE :key LIMIT 10];
6     }
7 }

 contactListSearchWithWireService.html:展現搜索出來的contact的信息,上面有一個輸入框以及兩個按鈕,點擊search進行搜索,點擊refresh清空緩存從新獲取。

 1 <template>
 2     <lightning-card icon-name="custom:custom63">
 3         <div class="slds-m-around_medium">
 4             <lightning-input type="search" class="slds-m-bottom_small" label="Search" value={searchKey}></lightning-input>
 5             <lightning-button-group>
 6                 <lightning-button variant="brand" label="search" onclick={handleSearch}></lightning-button>
 7                 <lightning-button variant="brand" label="Refresh" onclick={handleRefresh}></lightning-button>
 8             </lightning-button-group>
 9             <template if:true={contacts}>
10                 <template for:each={contacts} for:item="contact">
11                     <p key={contact.Id}>{contact.Name}</p>
12                 </template>
13             </template>
14             <template if:true={error}>
15                 <!-- TODO -->
16             </template>
17         </div>
18     </lightning-card>
19 </template>

contactListSearchWithWireService.js:這裏須要注意的一點是咱們使用wire裝載的一個方法名字是wiredContacts,爲了後期能夠針對方法使用refreshApex,咱們設置了storedContacts變量,而且在wire方法中設置了值,針對refreshApex方法咱們更新此變量。由於咱們在searchKey使用了$符號,標識它是reactive的,變化之後會從新執行方法,因此咱們點擊search時只須要賦值searchKey變量即可以達到調用wire方法從新讀取數據的做用了。

 1 import { LightningElement, track,wire } from 'lwc';
 2 import findContacts from '@salesforce/apex/ContactController.findContacts';
 3 import {refreshApex} from '@salesforce/apex'
 4 export default class ContactListSearchWithWireService extends LightningElement {
 5     @track searchKey;
 6     @track contacts;
 7     @track errors;
 8     storedContacts;
 9 
10     @wire(findContacts, { searchKey: '$searchKey' })
11     wiredContacts(storedContacts) {
12         this.storedContacts = storedContacts;
13         const {data,error} = storedContacts;
14         if(data) {
15             this.contacts = data;
16             this.errors = undefined;
17         } else if(error) {
18             this.errors = error;
19             this.contacts = undefined;
20         }
21     }
22 
23     handleSearch(event) {
24         this.searchKey = this.template.querySelector('lightning-input').value;
25     }
26 
27     handleRefresh(event) {
28         refreshApex(this.storedContacts);
29     }
30 }

 結果展現:

1) 當咱們輸入sa yang點擊search 之後會搜索出來sa yang數據,即便後期sa yang這條數據有改掉不符合要求,點擊search還會搜索出來,由於有緩存。

2) 當咱們點擊refresh之後,更改過的數據將再也不展現在結果區域。

 上面的demo咱們使用wire裝載函數以及針對函數狀況下使用apexRefresh的方式。下面的demo爲使用wire裝載變量而且使用apexRefresh刷新變量緩存的demo。

 contactListSearchWithWireProperty.html:由於咱們後臺返回的是變量,因此咱們針對for:each使用須要property.data方式。

 1 <template>
 2     <lightning-card icon-name="custom:custom63">
 3         <div class="slds-m-around_medium">
 4             <lightning-input type="search" class="slds-m-bottom_small" label="Search" value={searchKey}></lightning-input>
 5             <lightning-button-group>
 6                 <lightning-button variant="brand" label="search" onclick={handleSearch}></lightning-button>
 7                 <lightning-button variant="brand" label="Refresh" onclick={handleRefresh}></lightning-button>
 8             </lightning-button-group>
 9             <template if:true={contacts.data}>
10                 <template for:each={contacts.data} for:item="contact">
11                     <p key={contact.Id}>{contact.Name}</p>
12                 </template>
13             </template>
14             <template if:true={conacts.error}>
15                 <!-- TODO -->
16             </template>
17         </div>
18     </lightning-card>
19 </template>
ContactListSearchWithWireProperty.js:若是wire裝載的是變量,咱們直接在refreshApex方法傳遞此變量便可。
 1 import { LightningElement, track,wire } from 'lwc';
 2 import findContacts from '@salesforce/apex/ContactController.findContacts';
 3 import {refreshApex} from '@salesforce/apex'
 4 export default class ContactListSearchWithWireProperty extends LightningElement {
 5     @track searchKey;
 6 
 7     @wire(findContacts, { searchKey: '$searchKey' })
 8     contacts;
 9 
10     handleSearch(event) {
11         this.searchKey = this.template.querySelector('lightning-input').value;
12     }
13 
14     handleRefresh(event) {
15         refreshApex(this.contacts);
16     }
17 }

兩個demo的顯示效果相同,這裏很少作展現。

2) 使用命令方式調用後臺方法。

咱們使用wire方式操做後臺的apex經過上面的兩個例子能夠很好的理解了,可是使用wire方式有一個大的前置條件,須要後臺的方法聲明cacheable=true。

咱們針對數據獲取的方法使用wire方式很好,可是針對DML操做的方法不能使用cacheable=true就只能使用咱們這種命令方式的訪問後臺的方式。

此種方式的固定寫法爲:

1 methodName({ param : parameterObject })
2 .then(result => {
3 this.message = result;
4 this.error = undefined;
5 })
6 .catch(error => {
7 this.message = undefined;
8 this.error = error;
9 });

後臺的要求區別已經說完了,再說一下前端的區別。使用wire方式返回的是一個stream data,而且參數是reactive的,只要參數改變,就會自動觸發wire。使用上述方式返回的是promise,此種方式只能當次調用有效,若是後期有變化,則須要從新調用。
另一點爲refreshApex只能用在wire裝載的方法和變量,使用此種方式不支持此方法。

參數部分爲可選項,若是不傳遞參數則直接methodName()。若是傳遞參數使用{}方式傳遞便可。

咱們仍是使用上面的例子,只是把JS端改爲以命令方式編寫,html端重複內容再也不粘貼(須要去掉refresh按鈕),以下:

 1 import { LightningElement,track } from 'lwc';
 2 import findContacts from '@salesforce/apex/ContactController.findContacts';
 3 export default class ContactListSearchWithImperative extends LightningElement {
 4     @track searchKey;
 5     @track contacts;
 6     @track errors;
 7 
 8     handleSearch() {
 9         this.searchKey = this.template.querySelector('lightning-input').value;
10         findContacts({searchKey:this.searchKey})
11             .then(result => {
12                 this.contacts = result;
13                 this.errors = undefined;
14             })
15             .catch(error =>{
16                 this.errors = error;
17                 this.contacts = undefined;
18             });
19     }
20 }

效果展現同上面相同。

三. Configuration File Tag

咱們在建立一個LWC component時,會默認生成一個html / js /meta xml文件,其中meta xml 會指定LWC component不少配置特性,好比當前的LWC component能夠引用在哪一種類型的lightning page中,api version等配置信息。主要配置項有如下幾點:

apiVersion: 指定LWC component的api version;

description:當前LWC component的描述信息,當顯示在lightning app builder或者community builder的列表中咱們鼠標移動到上面會展現此描述信息,此配置項是一個可選項,沒必要填;

isExposed:用來指定當前的component是否能夠暴露給lightning app builder或者community builder使用;此標籤很像aura中針對aura:component的implements的用法;

masterLabel: 用來指定當前的component在lightning app builder或者community builder顯示的名字。默認名字顯示的是定義的component的API name,若是咱們想在列表初顯示須要顯示的名字,咱們能夠設置此字段。和description同樣,這個是可選項,非必填;

 targets:用來指定當前的component在哪裏能夠添加。當咱們指定isExposed爲true時,則必需要有targets信息。targets下面有taget子標籤,用來標識當前的component能夠加在什麼類型的lightning page中。target的可選值以下:

  • lightning__AppPage:容許當前的component在lightning app builder使用在app page中;
  • lightning__HomePage:容許當前的component在lightning app builder使用在home page中;
  • lightning__RecordPage:容許當前的component在lightning app builder使用在record page中;
  • lightning__Inbox:容許當前的component在lightning app builder中使用,用於爲outlook/gmail集成添加email 應用窗;
  • lightningCommunity__Page:容許當前的component在community builder中使用在lightning community page;
  • lightningCommunity__Default:和lightningCommunity__Page共同使用。添加此項能夠包括可配置的變量當這個component引用的時候;
  • lightningSnapin__ChatMessage:容許在Embedded Service Chat Setup中選擇此component。

咱們在項目中經常使用的配置就是lightning__AppPage / lightning__HomePage / lightning__RecordPage了。

 targetConfigs:用來配置不一樣類型的lightning page和不一樣的初始化的component 變量。一個lwc component可能有不少的變量聲明,咱們針對不一樣類型的lightning page中須要初始化不一樣的變量,即可以使用此標籤去實現。和上面的targets同樣,他也是一個父標籤,內容在子標籤targetConfig 聲明。targetConfig有一個屬性叫作targets,咱們可使用此屬性去聲明當前的配置項針對哪一個類型的lightning page,針對多個類型的lightning page,咱們可使用逗號','分隔。例如:

<targetConfig targets="lightning__RecordPage,lightning__AppPage">

 targetConfig下能夠配置三個子標籤,分別是property / objects / supportedFormFactors,用來給變量進行初始化操做。下面對這三個子標籤分別描述。

 1. Property: 咱們在LWC js中會使用@api標籤聲明public變量,使用Property在引用在lightning app builder或者community builder的時候咱們能夠設置一些初始值以及初始化配置。Property有如下的屬性:

  • type:用來聲明變量的類型,好比 Integer/ String / Boolean .
  • required:用來標識當前的變量是否必須設置。默認值爲false;
  • placeholder: 僅用於type爲String的狀況,用於當輸入框爲空的時候在輸入框中展現的提示信息;
  • name:咱們在js中聲明的變量名稱,二者必須徹底匹配。
  • min: 當type爲Integer的時候,設置咱們想要設置變量的最小值;
  • max: 當type爲Integer的時候,設置咱們想要設置變量的最大值;
  • label:在工具中展現attribute的顯示的label 名稱;
  • description:在工具中展現attribute的描述信息;
  • default:當前attribute的默認值;
  • datasource:當type爲string狀況下須要渲染變量爲picklist,則使用此屬性,不一樣之間的字符串使用逗號','分割。

上述的屬性中,只有name以及type是必填項,其餘都是可選項。

2. objects:當咱們在target中聲明當前的LWC component在targetConfig中配置了能夠引用在lightning record page時,咱們能夠指定當前的component能夠用於哪些objects使用。
同targetConfigs同樣,他也有一個子標籤叫作object用來聲明能夠用在哪一個object中使用。object標籤不能使用*來聲明能夠引用全部objects

 3. supportedFormFactors:咱們訪問salesforce可能使用手機或者電腦,咱們針對不一樣的媒介訪問不一樣的頁面(home page/ record page等)可能須要展現不一樣的大小,這時咱們就須要設置supportedFormFactors了。針對此配置項的配置信息,詳情能夠查看https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.use_config_form_factors

總結:篇中主要介紹的是LDS在LWC中的使用方式以及在LDS功能沒法知足狀況下,如何使用wire service以及訪問後臺方法進行加強。篇中有引入salesforce/ lightning/ui*Api甚至PageReference等信息下篇LWC內容會詳細闡述。篇中有錯誤的地方歡迎指出,有不懂的歡迎留言。

相關文章
相關標籤/搜索