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内容会详细阐述。篇中有错误的地方欢迎指出,有不懂的欢迎留言。

相关文章
相关标签/搜索