Salesforce LWC学习(七) Navigation & Toast


上一篇我们介绍了针对LWC中常用的LDS的适配的wire service以及@salesforce模块提供的相关的service,其实LWC中还提供其他的好用的service,比如针对导航相关的lightning/navigation以及展示toast提示信息相关的lightning/platformShowToastEvent。此篇主要针对这两个service进行简单的介绍。

一. lightning/navigation

我们有好多场景会用到跳转操作,比如创建记录以后跳转到此记录详情页,点击连接跳转到下载页面或者直接下载文件,跳转到某个列表等等。LWC中封装了lightning/navigation service去实现相关的跳转操作。当然,此service除了封装了跳转相关的功能,还封装了获取当前PageReference的功能。详情如下所示。

1. CurrentPageReference:此方法用于获取当前页面的引用,使用以下的声明便可以获取到当前的页面的引用了。如果我们想编辑其键值对的parameter,我们需要更新当前page reference的Page state。至于如何更新,下面会有介绍。

1 import { CurrentPageReference } from 'lightning/navigation';
2 @wire(CurrentPageReference)
3 pageRef;

返回的pageRef为PageReference变量。PageReference为js封装的对象,有三个变量。
1) type:当前PageReference的类型。lwc封装了以下的类型:

  • Lightning Component
  • Knowledge Article
  • Login Page
  • Named Page
  • Navigation Item Page
  • Object Page
  • Record Page
  • Record Relationship Page
  • Web Page

这些PageReference详情可以参看:https://developer.salesforce.com/docs/component-library/documentation/lwc/lwc.reference_page_reference_type
我们常用的有Lightning Component / Object Page / Record Page / Record Relationship Page / Web Page。
2)attribute:声明不同类型的PageReference创建PageReference需要配置不同的attribute,细节的attribute的配置同看上面链接。

3)state:用来存储键值对类型的parameter。我们在URL中可能传递参数,使用CurrentPageReference获取到的PageReference中,state存储的便是其参数部分。

下面来一个demo更好的了解用法以及返回内容。

getCurrentPageReferenceDemo.js:装载CurrentPagReference,将引用赋值给pageRef;
 1 import { LightningElement, wire } from 'lwc';
 2 import {CurrentPageReference} from 'lightning/navigation';
 3 export default class GetCurrentPageReferenceDemo extends LightningElement {
 4     @wire(CurrentPageReference)
 5     pageRef;
 6 
 7     get currentPageInfo() {
 8         if(this.pageRef !== undefined) {
 9             return JSON.stringify(this.pageRef);
10         }
11         return '';
12     }
13 }

getCurrentPageReferenceDemo.html:

1 <template>
2     {currentPageInfo}
3 template>

显示结果:我们将此component放在contacts的详情页里面,并且针对详情页的URL手动设置了parameter,返回的结果如下图所示。

 2. NavigationMixin:使用此adapter实现跳转功能。此adapter封装了两个API实现跳转。

  • [NavigationMixin.Navigate](pageReference, [replace]) - 在应用中一个component调用此API跳转到另外一个页面;
  • [NavigationMixin.GenerateUrl](pageReference) - component调用此API获取需要跳转的URL的Promise。

这两个API,先调用GenerateUrl获取到Promise,然后使用Navigate的API即可实现跳转。我们使用此service前需要先在头引入,和其他的区别为我们还需要在javascript的class中去继承NavigationMixin。

import { NavigationMixin } from 'lightning/navigation';
export default class MyCustomElement extends NavigationMixin(LightningElement) {}

上面的两个API我们也可以看见pageReference参数,此参数和上面的CurrentPageReference返回值类型相同,均为javascript中封装的CurrentPageReference对象。我们可以使用plain object去拼接此对象的变量。说的比较绕,下面通过一个官方的demo便可以更好的了解。

 navigationLineExample.js:在connectedCallback生命周期处声明了PageReference的Promise,用于handleClick时去触发。

 1 import { LightningElement, track } from 'lwc';
 2 import { NavigationMixin } from 'lightning/navigation';
 3 
 4 export default class NavigationLinkExample extends NavigationMixin(LightningElement) {
 5     @track url;
 6 
 7     connectedCallback() {
 8         // Store the PageReference in a variable to use in handleClick.
 9         // This is a plain Javascript object that conforms to the
10         // PageReference type by including "type" and "attributes" properties.
11         // The "state" property is optional.
12         this.accountHomePageRef = {
13             type: "standard__objectPage",
14             attributes: {
15                 "objectApiName": "Account",
16                 "actionName": "home"
17             }
18         };
19         this[NavigationMixin.GenerateUrl](this.accountHomePageRef)
20             .then(url => this.url = url);
21     }
22 
23     handleClick(evt) {
24         // Stop the event's default behavior.
25         // Stop the event from bubbling up in the DOM.
26         evt.preventDefault();
27         evt.stopPropagation();
28         // Navigate to the Account Home page.
29         this[NavigationMixin.Navigate](this.accountHomePageRef);
30     }
31 }

navigationLineExample.html:点击按钮触发handleClick方法

1 <template>
2     <div>
3         <a href={url} onclick={handleClick}>Account Homea>
4     div>
5 template>

将demo配置在account的record page中,通过上面的demo我们便可以实现跳转到Account的home页面的功能,也可以看到声明 PageReference中的type以及attributes的神奇之处。原理了解以后只需要通过改变PageReference Type以及其对应的attribute我们便可以实现更多的功能,感兴趣的小伙伴可以自行尝试。

 我们在跳转或者自刷新时,有时需要传递参数,在LWC中上面也提到过使用state变量传递参数,我们在更新此变量前先了解一下相关的限制和要求。

  • pagereference对象已冻结,因此不能直接更改它。如果我们想要跳转到一个同样的页面并且这个页面包含了已经更新了的state,我们需要copy一下当前的这个PageReference然后使用Object.assign({}, pageReference)方式去更新state,如果跳转到不同的页面,我们只需要创建plain PageReference的时候传进去即可,就不会有此点所说的限制。
  • state变量在构建键值对时,键必须使用namespace加上两个下划线__作为前缀,如果不是managed package,则namespace为c,比如我们想要传递testParam的值为testValue。则构建键值对应该为state.c__testParam = testValue;
  • state变量中的键值对的value必须全部为string类型因为state变量的键值对均可以序列化到URL query parameter。
  • 如果想要删除state的某个键值对,只需要设置value为undefined即可。

通过上述1,2,3,4点我们可以看出来,1属于限制,2,3,4属于规范。我们针对跳转到其他页面直接设置state即可,如果针对跳转到当前页面,只是参数变化,便需要考虑上面的第一点。通过官方提供的demo可以更好的了解针对parameter的处理。

 pageStateChangeExample.js:demo中考虑的时跳转到当前页面,只是参数的变化的处理。此时pagereference已经冻结,只能通过Object.assign方法去处理。

 1 import { LightningElement, wire, track } from 'lwc';
 2 import { CurrentPageReference, NavigationMixin } from 'lightning/navigation';
 3 
 4 export default class PageStateChangeExample extends NavigationMixin(LightningElement) {
 5 
 6     // Injects the page reference that describes the current page
 7     @wire(CurrentPageReference)
 8     setCurrentPageReference(currentPageReference) {
 9         this.currentPageReference = currentPageReference;
10 
11         if (this.connected) {
12             // We need both the currentPageReference, and to be connected before
13             // we can use NavigationMixin
14             this.generateUrls();
15         } else {
16             // NavigationMixin doesn't work before connectedCallback, so if we have 
17             // the currentPageReference, but haven't connected yet, queue it up
18             this.generateUrlOnConnected = true;
19         }
20     }
21     @track
22     showPanelUrl;
23 
24     @track
25     noPanelUrl;
26 
27     // Determines the display for the component's panel
28     get showPanel() {
29         // Derive this property's value from the current page state
30         return this.currentPageReference &&
31             this.currentPageReference.state.c__showPanel == 'true';
32     }
33 
34     generateUrls() {
35         this[NavigationMixin.GenerateUrl](this.showPanelPageReference)
36             .then(url => this.showPanelUrl = url);
37         this[NavigationMixin.GenerateUrl](this.noPanelPageReference)
38             .then(url => this.noPanelUrl = url);
39     }
40 
41     // Returns a page reference that matches the current page
42     // but sets the "c__showPanel" page state property to "true"
43     get showPanelPageReference() {
44         return this.getUpdatedPageReference({
45             c__showPanel: 'true' // Value must be a string
46         });
47     }
48 
49     // Returns a page reference that matches the current page
50     // but removes the "c__showPanel" page state property
51     get noPanelPageReference() {
52         return this.getUpdatedPageReference({
53             // Removes this property from the state
54             c__showPanel: undefined
55         });
56     }
57 
58     // Utility function that returns a copy of the current page reference
59     // after applying the stateChanges to the state on the new copy
60     getUpdatedPageReference(stateChanges) {
61         // The currentPageReference property is read-only.
62         // To navigate to the same page with a modified state,
63         // copy the currentPageReference and modify the copy.
64         return Object.assign({}, this.currentPageReference, {
65             // Copy the existing page state to preserve other parameters
66             // If any property on stateChanges is present but has an undefined
67             // value, that property in the page state is removed.
68             state: Object.assign({}, this.currentPageReference.state, stateChanges)
69         });
70     }
71 
72     connectedCallback() {
73         this.connected = true;
74         
75         // If the CurrentPageReference returned before this component was connected,
76         // we can use NavigationMixin to generate the URLs
77         if (this.generateUrlOnConnected) {
78             this.generateUrls();
79         }
80     }
81 
82     handleShowPanelClick(evt) {
83         evt.preventDefault();
84         evt.stopPropagation();
85         // This example passes true to the 'replace' argument on the navigate API
86         // to change the page state without pushing a new history entry onto the
87         // browser history stack. This prevents the user from having to press back
88         // twice to return to the previous page.
89         this[NavigationMixin.Navigate](this.showPanelPageReference, true);
90     }
91 
92     handleNoPanelClick(evt) {
93         evt.preventDefault();
94         evt.stopPropagation();
95         this[NavigationMixin.Navigate](this.noPanelPageReference, true);
96     }
97 }

pageStateChangeExample.html:针对showPanel以及noPanelUrl的处理

<template>
    <div>
        <a href={showPanelUrl} onclick={handleShowPanelClick}>Show Panela>
    div>
    <div if:true={showPanel}>
        <h1>This is the panelh1>
        <a href={noPanelUrl} onclick={handleNoPanelClick}>Hide Panela>
    div>
template>

 二. Lightning/platformShowToastEvent

当我们进行DML操作或者某种场景或者validation需要展示一些信息,当然我们可以通过ligtning-messages展示信息在页面上,另外一种方式可以使用toast方式仅显示几秒的提示信息随后消失的样式。LWC提供了lightning/platformShowToastEvent实现此功能。模块中封装了ShowToastEvent事件,实例化以后dispatchEvent即可使用。

ShowToastEvent事件有以下的几个参数

  • title:toast的标题部分,展示在header区域;
  • message:toast的内容部分。此message可以是一个纯文本,也可以是一个包含place holder的文本,place holder通常是URL或者文本;
  • messageData:当message中包含{} place holder时,使用messageData进行替换;
  • variant:toast中展示的theme以及icon的样式。有几个值供选择:info / success / warning / error。我们可以根据不同的场景显示不同的variant进行展示;
  • mode:用来决定toast展示多长时间。有几个值供选择:dismissable/pester/sticky。dismissable是默认的值,功能为用户点击关闭按钮或者经过3秒以后toast消失;pester用于只显示3秒,3秒以后自动消失(此种没有关闭按钮);sticky用于只有用户点击关闭按钮才会消失。

我们在recordEditFormSample展示此用法。

 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                 mode:"pester"
18             });
19             this.dispatchEvent(evt);
20             return;
21         }
22         this.template.querySelector('lightning-record-edit-form').submit(fields);
23     }
24 
25     handleLoad(event) {
26         console.log('execute load');
27     }
28 
29     handleSuccess(event) {
30         const evt = new ShowToastEvent({
31             title: "Account Operated Success",
32             message: "Record is :" + event.detail.id,
33             variant: "success"
34         });
35         this.dispatchEvent(evt);
36     }
37 
38     handleError(event) {
39         const evt = new ShowToastEvent({
40             title: "Account Operated Failed",
41             message: event.detail.message,
42             variant: "error",
43             mode: "sticky"
44         });
45         this.dispatchEvent(evt);
46     }
47 
48     handleReset(event) {
49         const inputFields = this.template.querySelectorAll(
50             'lightning-input-field'
51         );
52         if (inputFields) {
53             inputFields.forEach(field => {
54                 field.reset();
55             });
56         }
57      }
58 }

效果展示:

 sticky样式,只有点击关闭按钮才能消失toast。

 总结:此篇主要说的是Navigation以及Toast相关的知识,其中Navigation可以根据不同的type以及attribute去做不同功能的跳转以及下载等操作,篇中例举的比较少,感兴趣的可以深入学习。篇中有错误地方欢迎指出,有不懂的欢迎留言。