// c/myApp.js
import { LightningElement } from "lwc";
import LightningAlert from "lightning/alert";
export default class MyApp extends LightningElement {
async handleAlertClick() {
await LightningAlert.open({
message: "This is the alert message.",
theme: "error", // a red theme intended for error states
label: "Error!", // this is the header text
});
// alert modal has been closed
}
}
// c/myApp.js
import { LightningElement } from "lwc";
import LightningConfirm from "lightning/confirm";
export default class MyApp extends LightningElement {
async handleConfirmClick() {
const result = await LightningConfirm.open({
message: "This is the confirmation message.",
variant: "headerless",
label: "This is the aria-label value",
// label value isn't visible in the headerless variant
});
// confirm modal has been closed
}
}
import { LightningElement } from "lwc";
import LightningPrompt from "lightning/prompt";
export default class MyApp extends LightningElement {
handlePromptClick() {
LightningPrompt.open({
message: "This is the prompt message.",
//theme defaults to "default"
label: "Please Respond!", // this is the header text
defaultValue: "Optional initial input value",
}).then((result) => {
// prompt modal has been closed
});
}
}
// navigationLinkExample.js
import { LightningElement, wire } from "lwc";
import { NavigationMixin } from "lightning/navigation";
export default class NavigationLinkExample extends NavigationMixin(LightningElement) {
url;
connectedCallback() {
// Store the PageReference in a variable to use in handleClick.
// This is a plain Javascript object that conforms to the
// PageReference type by including 'type' and 'attributes' properties.
// The 'state' property is optional.
this.accountHomePageRef = {
type: "standard__objectPage",
attributes: {
objectApiName: "Account",
actionName: "home",
},
};
this[NavigationMixin.GenerateUrl](this.accountHomePageRef).then((url) => (this.url = url));
}
handleClick(evt) {
// Stop the event's default behavior.
// Stop the event from bubbling up in the DOM.
evt.preventDefault();
evt.stopPropagation();
// Navigate to the Account Home page.
this[NavigationMixin.Navigate](this.accountHomePageRef);
}
}
添加查询参数
若要向 URL 添加查询参数,请更新该属性。导航服务使用 a 来生成 URL。属性的键值对将序列化为 URL 查询参数。查询参数描述页面并形成用户可以保存或添加书签的更具体的 URL。PageReference.statePageReferencestate
// pageStateChangeExample.js
import { LightningElement, wire } from "lwc";
import { CurrentPageReference, NavigationMixin } from "lightning/navigation";
export default class PageStateChangeExample extends NavigationMixin(LightningElement) {
// Declare the currentPageReference variable in order to track it
currentPageReference;
// Injects the page reference that describes the current page
@wire(CurrentPageReference)
setCurrentPageReference(currentPageReference) {
this.currentPageReference = currentPageReference;
if (this.connected) {
// We need to have the currentPageReference, and to be connected before
// we can use NavigationMixin
this.generateUrls();
} else {
// NavigationMixin doesn't work before connectedCallback, so if we have
// the currentPageReference, but haven't connected yet, queue it up
this.generateUrlOnConnected = true;
}
}
showPanelUrl;
noPanelUrl;
// Determines the display for the component's panel
get showPanel() {
// Derive this property's value from the current page state
return this.currentPageReference && this.currentPageReference.state.c__showPanel == "true";
}
generateUrls() {
this[NavigationMixin.GenerateUrl](this.showPanelPageReference).then(
(url) => (this.showPanelUrl = url),
);
this[NavigationMixin.GenerateUrl](this.noPanelPageReference).then(
(url) => (this.noPanelUrl = url),
);
}
// Returns a page reference that matches the current page
// but sets the 'c__showPanel' page state property to 'true'
get showPanelPageReference() {
return this.getUpdatedPageReference({
c__showPanel: "true", // Value must be a string
});
}
// Returns a page reference that matches the current page
// but removes the 'c__showPanel' page state property
get noPanelPageReference() {
return this.getUpdatedPageReference({
// Removes this property from the state
c__showPanel: undefined,
});
}
// Utility function that returns a copy of the current page reference
// after applying the stateChanges to the state on the new copy
getUpdatedPageReference(stateChanges) {
// The currentPageReference property is read-only.
// To navigate to the same page with a modified state,
// copy the currentPageReference and modify the copy.
return Object.assign({}, this.currentPageReference, {
// Copy the existing page state to preserve other parameters
// If any property on stateChanges is present but has an undefined
// value, that property in the page state is removed.
state: Object.assign({}, this.currentPageReference.state, stateChanges),
});
}
connectedCallback() {
this.connected = true;
// If the CurrentPageReference returned before this component was connected,
// we can use NavigationMixin to generate the URLs
if (this.generateUrlOnConnected) {
this.generateUrls();
}
}
handleShowPanelClick(evt) {
evt.preventDefault();
evt.stopPropagation();
// This example passes true to the 'replace' argument on the navigate API
// to change the page state without pushing a new history entry onto the
// browser history stack. This prevents the user from having to press back
// twice to return to the previous page.
this[NavigationMixin.Navigate](this.showPanelPageReference, true);
}
handleNoPanelClick(evt) {
evt.preventDefault();
evt.stopPropagation();
this[NavigationMixin.Navigate](this.noPanelPageReference, true);
}
}
导航到不同的页面类型
导航服务支持 Lightning 中不同类型的页面。每种类型都支持一组不同的特性和状态属性。这两个 API 都支持这些类型。PageReferencePageReference
示例:导航到页面
让我们看一个示例,说明如何创建导航到页面的按钮。
我们的示例有一个组件,它是 Salesforce 应用程序中的一个页面。在页面底部,有一个带有标签 Next 的组件。可以在每个页面的底部使用该组件,让用户浏览应用程序。welcomenavtabnavtab
<!-- welcome.html -->
<!-- lots and lots of code removed for brevity -->
<div class="slds-m-vertical_large">
<c-navtab tab-name="lightning_components" label="Next"></c-navtab>
</div>
该组件自定义组件以导航到应用程序中的下一页。使用者指定 a 和 a 。c-nav-tablightning-buttontab-namelabel
// navigationToPagesExample.js
import { LightningElement, wire } from "lwc";
import { NavigationMixin } from "lightning/navigation";
export default class NavigationToPagesExample extends NavigationMixin(LightningElement) {
navigateToObjectHome() {
// Navigate to the Case object home page.
this[NavigationMixin.Navigate]({
type: "standard__objectPage",
attributes: {
objectApiName: "Case",
actionName: "home",
},
});
}
navigateToListView() {
// Navigate to the Contact object's Recent list view.
this[NavigationMixin.Navigate]({
type: "standard__objectPage",
attributes: {
objectApiName: "Contact",
actionName: "list",
},
state: {
// 'filterName' is a property on the page 'state'
// and identifies the target list view.
// It may also be an 18 character list view id.
filterName: "Recent", // or by 18 char '00BT0000002TONQMA4'
},
});
}
navigateToNewRecordPage() {
// Opens the new Account record modal
// to create an Account.
this[NavigationMixin.Navigate]({
type: "standard__objectPage",
attributes: {
objectApiName: "Account",
actionName: "new",
},
});
}
navigateToRecordViewPage() {
// View a custom object record.
this[NavigationMixin.Navigate]({
type: "standard__recordPage",
attributes: {
recordId: "a03B0000002tEurIAE",
objectApiName: "namespace__ObjectName", // objectApiName is optional
actionName: "view",
},
});
}
navigateToRecordEditPage() {
// Opens the Account record modal
// to view a particular record.
this[NavigationMixin.Navigate]({
type: "standard__recordPage",
attributes: {
recordId: "001B000000ZBz22IAD",
objectApiName: "Account", // objectApiName is optional
actionName: "edit",
},
});
}
navigateToRelatedList() {
// Navigate to the CaseComments related list page
// for a specific Case record.
this[NavigationMixin.Navigate]({
type: "standard__recordRelationshipPage",
attributes: {
recordId: "500xx000000Ykt4AAC",
objectApiName: "Case",
relationshipApiName: "CaseComments",
actionName: "view",
},
});
}
navigateToTabPage() {
// Navigate to a specific CustomTab.
this[NavigationMixin.Navigate]({
type: "standard__navItemPage",
attributes: {
// CustomTabs from managed packages are identified by their
// namespace prefix followed by two underscores followed by the
// developer name. E.g. 'namespace__TabName'
apiName: "CustomTabName",
},
});
}
navigateToWebPage() {
// Navigate to a URL
this[NavigationMixin.Navigate](
{
type: "standard__webPage",
attributes: {
url: "http://salesforce.com",
},
},
true, // Replaces the current page in your browser history with the URL
);
}
}
lightning/pageReferenceUtils 模块提供了 and 函数,用于将默认字段值编码为字符串并对其进行解码。将编码字符串分配给页面引用中的属性。encodeDefaultFieldValues()decodeDefaultFieldValues()pageReference.state.defaultFieldValuesstandard__objectPage
该调用将创建一个带有“Press Me!”标签的按钮。该按钮入到 ID 为 的 DOM 元素中。在页面上添加按钮并处于活动状态后,将调用回调函数并执行语句。回调接收创建的组件作为其唯一参数。在这个简单示例中,该按钮未配置为执行任何操作。$Lightning.createComponent()lightningvfconsole.log()
<targetConfigs>
<targetConfig targets="lightningCommunity__Default">
<property
name="recordId"
type="String"
label="Record Id"
description="Automatically bind the page's record id to the component variable"
default="{!recordId}"
/>
</targetConfig>
</targetConfigs>
若要获取记录 ID 和对象 API 名称,请公开 and 作为属性。请参阅使组件了解其记录上下文和使组件了解其对象上下文。recordIdobjectApiName
此示例显示当前记录的记录 ID 和对象 API 名称。它还返回当前页面引用,该引用描述当前页面及其状态。
<template>
<p>These two fields are auto-populated based on the record context:</p>
<p>RecordId: <i>{recordId}</i>, objectApiName: <i>{objectApiName}</i></p>
<p>{pageRefString}</p>
</template>
import { LightningElement, api, wire } from "lwc";
import { CurrentPageReference } from "lightning/navigation";
export default class RecordContextAction extends LightningElement {
@api recordId;
@api objectApiName;
@wire(CurrentPageReference)
pageRef;
get pageRefString() {
return JSON.stringify(this.pageRef);
}
}
创建 Headless 快速操作
无头快速操作在 Lightning Web 组件中执行自定义代码。与屏幕操作不同,无头操作不会打开模式窗口。
要使您的组件用作无外设快速操作,请配置目标。请参阅配置组件以执行快速操作。
与记录页面上的其他 Lightning Web 组件不同,LWC 快速操作不会传入 .如果需要访问 ,请在代码中设置 的值。recordIdconnectedCallback()recordIdrecordId
_recordId;
set recordId(recordId) {
if (recordId !== this._recordId) {
this._recordId = recordId;
实现 invoke()
在您的 Lightning Web 组件中,始终公开为无头快速操作的公共方法。每次触发快速操作时,都会执行该方法。invoke()invoke()
import { LightningElement, api } from "lwc";
export default class HeadlessSimple extends LightningElement {
@api invoke() {
console.log("Hi, I'm an action.");
}
}
为您的 Lightning Web 组件创建一个空模板。
<template> </template>
若要防止在长时间运行的操作中多次并行执行快速操作,请添加内部布尔标志。
的返回类型为 。返回 a 会使方法异步,但返回的方法将被忽略。invoke()voidPromisePromise
此代码使用布尔标志来阻止双重执行,并使用 a 来等待完成。即使返回类型为 ,代码也会异步执行。Promisesleepvoid
import { LightningElement, api } from "lwc";
export default class HeadlessAsync extends LightningElement {
isExecuting = false;
@api async invoke() {
if (this.isExecuting) {
return;
}
this.isExecuting = true;
await this.sleep(2000);
this.isExecuting = false;
}
sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
导航
要导航到 Lightning Experience 中的其他页面、记录或列表,请使用导航服务。
此示例导航到联系人主页。
import { LightningElement, api } from "lwc";
import { NavigationMixin } from "lightning/navigation";
export default class navigateToRecordAction extends NavigationMixin(LightningElement) {
@api invoke() {
this[NavigationMixin.Navigate]({
type: "standard__objectPage",
attributes: {
objectApiName: "Contact",
actionName: "home",
},
});
}
}
import { LightningElement, api } from "lwc";
import { ShowToastEvent } from "lightning/platformShowToastEvent";
export default class DispatchEventAction extends LightningElement {
@api async invoke() {
let event = new ShowToastEvent({
title: "I am a headless action!",
message: "Hi there! Starting...",
});
this.dispatchEvent(event);
await this.sleep(2000);
event = new ShowToastEvent({
title: "I am a headless action!",
message: "All done!",
});
this.dispatchEvent(event);
}
sleep(ms) {
// eslint-disable-next-line @lwc/lwc/no-async-operation
return new Promise((resolve) => setTimeout(resolve, ms));
}
}
<!--htmlEmailEditor.js-meta.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>49.0</apiVersion>
<!--isExposed can be true or false-->
<isExposed>true</isExposed>
</LightningComponentBundle>
// volumeEditor.js
import { LightningElement, api } from "lwc";
export default class VolumeEditor extends LightningElement {
_inputVariables = [];
@api
get inputVariables() {
return this._inputVariables;
}
// Set a field with the data that was stored from the flow.
// This data includes the public volume property of the custom volume
// component.
set inputVariables(variables) {
this._inputVariables = variables || [];
}
// Get the value of the volume input variable.
get volume() {
const param = this.inputVariables.find(({ name }) => name === "volume");
return param && param.value;
}
@api
validate() {
const volumeCmp = this.template.querySelector("lightning-slider");
const validity = [];
if (this.volume < 0 || this.volume > 100) {
volumeCmp.setCustomValidity("The slider range is between 0 and 100.");
validity.push({
key: "Slider Range",
errorString: "The slider range is between 0 and 100.",
});
} else {
volumeCmp.setCustomValidity("");
}
volumeCmp.reportValidity();
return validity;
}
handleChange(event) {
if (event && event.detail) {
const newValue = event.detail.value;
const valueChangedEvent = new CustomEvent("configuration_editor_input_value_changed", {
bubbles: true,
cancelable: false,
composed: true,
detail: {
name: "volume",
newValue,
newValueDataType: "Number",
},
});
this.dispatchEvent(valueChangedEvent);
}
}
}
<!--volumeEditor.js-meta.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>49.0</apiVersion>
<!--isExposed can be true or false-->
<isExposed>true</isExposed>
</LightningComponentBundle>
// SelectRecordAction.cls
public with sharing class SelectRecordAction {
@InvocableMethod(configurationEditor='c-select-record-editor')
public static List <Results> selectRecord(List<Requests> requestList) {
List<SObject> inputCollection = requestList[0].inputCollection;
//Store the first input record for output
SObject outputMember = inputCollection[0];
Results response = new Results();
response.outputMember = outputMember;
List<Results> responseWrapper= new List<Results>();
responseWrapper.add(response);
return responseWrapper;
}
public class Requests {
@InvocableVariable(label='Object for Input' description='Records for Input')
public List<SObject> inputCollection;
}
public class Results {
@InvocableVariable(label='Object for Storing Output' description='Records for Output')
public SObject outputMember;
}
}
<!--selectRecordEditor.js-meta.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>50.0</apiVersion>
<!--isExposed can be true or false-->
<isExposed>true</isExposed>
</LightningComponentBundle>
<!--displayRecordEditor.js-meta.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>50.0</apiVersion>
<!--isExposed can be true or false-->
<isExposed>true</isExposed>
</LightningComponentBundle>
<!---createContactActionEditor.js-meta.xml-->
<?xml version="1.0" encoding="UTF-8"?>
<LightningComponentBundle xmlns="http://soap.sforce.com/2006/04/metadata">
<apiVersion>53.0</apiVersion>
<!--isExposed can be true or false-->
<isExposed>true</isExposed>
</LightningComponentBundle>
自定义属性编辑器 JavaScript 接口
自定义属性编辑器使用这些 JavaScript 函数与 Flow Builder 进行通信。
输入变量
该接口提供有关 FlowScreenField 元数据的数据。inputVariables
_inputVariables = [];
@api
get inputVariables() {
return this._inputVariables;
}
// Set a field with the data that was stored from the flow.
set inputVariables(variables) {
this._inputVariables = variables || [];
}
_elementInfo = {};
@api
get elementInfo() {
return this._elementInfo;
}
// Set a local variable with the data that was stored from flow.
set elementInfo(info) {
this._elementInfo = info || {};
}
// myComponent.js
import { LightningElement } from "lwc";
export default class MyComponent extends LightningElement {
opportunityId;
accountId;
customerLocation;
get flowName() {
if (this.customerLocation === "USA") {
flowname = "Survey_USA";
} else {
flowname = "Survey_General";
}
}
get inputVariables() {
return [
{
// Match with the input variable name declared in the flow.
name: "OpportunityID",
type: "String",
// Initial value to send to the flow input.
value: this.opportunityId,
},
{
// Match with the input variable name declared in the flow.
name: "AccountID",
type: "String",
// Initial value to send to the flow input.
value: this.accountId,
},
];
}
handleStatusChange(event) {
if (event.detail.status === "FINISHED") {
// set behavior after a finished flow interview.
}
}
}
使用注意事项
该组件仅支持该属性的活动流。lightning-flowflow-api-name
该事件返回这些参数。onstatuschange
如果您的流程具有自定义 Lightning Web 组件或 Aura 组件,则无法在使用 Lightning Web Runtime 的 Experience Cloud 站点上使用。lightning-flow
参数
类型
描述
活动阶段
对象[]
流中 $Flow.ActiveStages 变量的当前值。在 API 版本 42.0 及更高版本中可用。
当前阶段
对象
流中 $Flow.CurrentStage 变量的当前值。在 API 版本 42.0 及更高版本中可用。
对于您设置的每个变量,请提供变量的名称、类型和值。对于 type (类型),请使用 API 名称作为流数据类型。例如,对于记录变量,请使用 SObject,对于文本变量,请使用 String。
{
name : "varName",
type : "flowDataType",
value : valueToSet
},
{
name : "varName",
type : "flowDataType",
value : [ value1, value2 ]
}, ...
JavaScript 文件中的此方法返回一个数字变量、一个日期集合变量和几个记录变量的值数组。Flow Builder 中的 Record 数据类型与此处的 SObject 相对应。
// myComponent.js
get inputVariables() {
return [
{ name: 'numVar', type: 'Number', value: 30 },
{ name: 'dateColl', type: 'String', value: [ '2016-10-27', '2017-08-01' ] },
// Sets values for fields in the account record (sObject) variable.
// Id uses the value of the accountId property. Rating uses a string.
{ name: 'account', type: 'SObject', value: {
'Id': this.accountId,
'Rating': 'Warm'
}},
// Set the contact record (sObject) variable to the value of the contact property.
// We're assuming the property contains the entire sObject for a contact record.
{ name: 'contact', type: 'SObject', value: this.contact }
]
}
// AccountController.apex
public with sharing class AccountController {
@AuraEnabled(cacheable=true)
public static Account getAccount() {
return [SELECT Id, Name, LastModifiedDate, FROM Account LIMIT 1];
}
}
// myComponent.js
import { LightningElement, track, wire } from "lwc";
import getAccount from "@salesforce/apex/AccountController.getAccount";
export default class MyComponent extends LightningElement {
@track
inputVariables = [];
@wire(getAccount)
getAccountFromApex({ error, data }) {
if (error) {
console.log("Failed to get account data.");
} else if (data) {
this.inputVariables = [
{
name: "account",
type: "SOjbect",
value: data,
},
];
}
}
}
// myComponent.js
handleStatusChange(event) {
if(event.detail.status === 'FINISHED') {
// Redirect to another page in Salesforce., or
// Redirect to a page outside of Salesforce., or
// Show a toast, or something else
}
}
// InterviewsController.apex
public class InterviewsController {
@AuraEnabled(cacheable=true)
public static String getPausedId() {
// Get the ID of the running user.
String currentUser = UserInfo.getUserId();
// Find all of that user’s paused interviews for the Survey customers flow.
List<FlowInterview> interviews =
[ SELECT Id FROM FlowInterview WHERE CreatedById = :currentUser AND
InterviewLabel LIKE '%Survey customers%'];
if (interviews == null || interviews.isEmpty()) {
return null; // early out
}
// Return the ID for the first interview in the list.
return interviews.get(0).Id;
}
}
// myComponent.js
import { LightningElement, wire } from "lwc";
import getPausedId from "@salesforce/apex/InterviewsController.getPausedId";
export default class MyComponent extends LightningElement {
flowName;
pausedInterviewId;
@wire(getPausedId)
getPausedInterviewId({ error, data }) {
if (error) {
// start a new interview since no interview id was returned.
this.flowName = "Survey_customers";
} else if (data) {
// resume the flow with the returned interview id.
this.pausedInterviewId = data;
}
}
}
通知运行时属性更改。Lightning Web 组件使用事件来报告从组件到流程的更改。为了通知流运行时属性更改,组件会触发事件。例如,在满足条件时使用条件可见性呈现屏幕组件时,或者将输出属性的值映射到流变量时,请使用此事件。有关代码示例,请参见。FlowAttributeChangeEventlightning-flow-support
设置屏幕流组件的反应性
屏幕流反应性意味着使屏幕流组件更改影响另一个同一屏幕组件。
在 Flow 构建器中添加和配置自定义 Lightning Web 组件之前,请创建自定义 LWC。
如果您计划构建反应式屏幕组件或使用支持的组件,则需要了解每个组件的注意事项。反应性包括:
确定哪个屏幕组件是源,哪个组件是反应式组件。
了解每组组件的不同交互。
示例:标准流量组件到标准流量组件
使用标准 Flow 组件是实现反应性的最简单方法。
使用标准 Flow 组件,您可以添加组件,为每个组件提供 API 名称和标签(如果需要),标识源和反应式组件,然后保存并运行流。
要使 Name 组件的 First Name 字段响应并显示来自 Text 组件的所有文本输入,请执行以下操作:
在流程中,添加组件和组件,并为每个组件提供标签。TextName
在组件的 First Name’s source 字段中,选择组件作为源。源可以是同一类型的任何组件,在本例中,Text 组件的类型和 Name 组件的 First Name 字段的类型为 String。NameText
保存并运行 Flow,然后在组件的输入中键入 .键入每个字符后,“名字”字段会立即更新。TextThis is Reactivity
示例:标准流组件到自定义 Lightning Web 组件
让您的自定义组件对标准 Flow 组件中的更改做出反应。
要创建自定义组件并将其添加到 Flow Builder 中,请执行以下操作:colorName
添加“文本”组件和“自定义”组件,并为每个组件提供标签。colorName
在 colorName 的属性中,选择组件作为“颜色名称”字段的源。Text
对文本输入所做的任何更改都会自动反映在自定义 Lightning Web 组件 .例如,在文本输入中输入洋红色会导致 LWC 显示具有相同颜色 magenta 样式的颜色名称。colorName
<!-- colorName.html -->
<template>
<p>
Color name has the same color :
<span style={colorStyle}>{color}</span>
</p>
</template>
// colorName.js
import { LightningElement, api } from "lwc";
export default class colorName extends LightningElement {
@api color;
get colorStyle() {
return "color:" + this.color;
}
}
@api textValue;
textValueToRender;
changeCounter = 0;
get textValue() {
return this.textValueToRender;
}
// Due to the nature of the Flow Runtime, this setter method
// is called after the FlowAttributeChangeEvent fired below. It also
// is invoked when reactivity leads to a change to the textValue property.
set textValue(newTextValue) {
this.changeCounter++;
this.textValueToRender = newTextValue;
}
handleTextInputChange(event) {
const event = new FlowAttributeChangeEvent('textValue', event.target.value);
this.dispatchEvent(event);
}
在这种情况下,每当用户在文本输入中输入更改以及 Flow Runtime 确定属性已更改时,都会递增。这种方法考虑了组件初始化和通过反应性进行的跨组件交互。使用中间变量呈现来自 set 方法的更新。在此示例中,填充此角色。this.changeCountertextValuetextValueToRender
//colorPicker.js
@api color;
inputValue;
selectedSwatchId;
ALL_SWATCHES =[{
'id': 1,
'color': 'red'
}, {
'id': 2,
'color': 'blue'
}, {
'id': 3,
'color': 'green'
}, {
'id': 4,
'color': 'yellow'
}];
set color(newColor) {
// Set the content of the input text to the color value:
this.inputValue = newColor;
// If the color matches a swatch, highlight it in the UI:
const foundSwatch = this.ALL_SWATCHES.find(swatch => swatch.color === newColor);
this.selectedSwatchId = foundSwatch && foundSwatch.id;
}
get color() {
return this.inputValue;
}
handleSwatchSelected(event) {
// Select get the swatch data for the clicked swatch:
const selectedSwatch = this.ALL_SWATCHES.find(swatch => swatch.id === event.target.dataset.id);
const changeEvent = new FlowAttributeChangeEvent('color', selectedSwatch.color);
this.dispatchEvent(changeEvent);
}
handleInputValueChange(event) {
this.inputValue = event.target.value;
const changeEvent = new FlowAttributeChangeEvent('color', this.inputValue);
this.dispatchEvent(changeEvent);
}
使用 get 方法将多个属性组合在一起,为视图构造派生变量。@api
//Salutation.js
@api firstName;
@api lastName;
@api ageName;
get salutation() {
return `Hello ${this.firstName} ${this.lastName}, you are ${this.age} years old.`;
}
const changeEvent = new FlowAttributeChangeEvent("prop", "value");
changeEvent.bubbles = false; // Don’t do this
changeEvent.composed = false; // Don’t do this
避免同时发生射击和事件,因为它可能导致争用条件。我们永远无法保证您的 Lightning Web 组件在导航过程开始时有时间呈现更新的值。FlowAttributeChangeEventsFlowNavigationXxx
务必在驱动更改的属性的方法中为派生属性触发事件。此外,在组件的方法中触发派生属性的事件,例如:FlowAttributeChangeEventsetFlowAttributeChangeEventconnectedCallback// 'Driving' attribute set method set stringToCount(newStringToCount) { this._stringToCount = newStringToCount; // Fire an event for 'derived' attribute const lengthChangeEvent = new FlowAttributeChangeEvent('stringLength', this._stringToCount.length); this.dispatchEvent(lengthChangeEvent); } connectedCallback() { // Fire an event for 'derived' attribute here as well const lengthChangeEvent = new FlowAttributeChangeEvent('stringLength', this.\_stringToCount.length); this.dispatchEvent(lengthChangeEvent); }
不要在方法中省略事件。 您必须将事件包含在两个位置。当流执行驱动更改的属性的 in 方法时,组件不在 DOM 中,事件基本上无处可去。在方法中添加事件可确保当流将组件添加到 DOM 时,流运行时可以使用该事件。FlowAttributeChangeEventconnectedCallbackFlowAttributeChangeEventFlowAttributeChangeEventsetconnectedCallback
不要在方法中省略事件。 触发驱动更改的属性的 in 方法可确保将更改应用于驱动更改的属性和派生属性。因此,包含自定义组件的反应性链可以正常工作。FlowAttributeChangeEventsetFlowAttributeChangeEventset
不要通过使用来延迟触发方法内部来省略该方法。 延迟可确保当流首次调用该方法时,组件位于 DOM 中。但是,延迟还意味着组件在流初始化屏幕后触发事件。因此,流运行时始终覆盖保留的值,并否决重新访问的值的设置。connectedCallbackPromise.resolve().then(...)FlowAttributeChangeEventsetFlowAttributeChangeEventsetset stringToCount(newStringToCount) { this._stringToCount = newStringToCount; const lengthChangeEvent = new FlowAttributeChangeEvent('stringLength', this._stringToCount.length); // This code overwrites preserved values. Don't do this. Promise.resolve().then(() => { this.dispatchEvent(lengthChangeEvent); }); }
导航更改事件最佳实践
除了 之外,您的 Lightning Web 组件还可以触发导航事件以在屏幕之间移动、暂停或完成流程。要使用导航事件,请从以下位置导入这些函数:FlowAttributeChangeEventlightning/flowSupport
将虚拟化概念深入到 Web 应用程序级别,主机环境是浏览器。Lightning Web Security 是在主机环境中运行的虚拟化引擎,用于创建和控制虚拟环境。命名空间 JavaScript 沙箱是虚拟环境。Lightning Web Security 授予每个虚拟环境对主机环境中特定资源的有限访问权限。这些资源包括全局对象、网络访问、Cookie 访问、本地存储等。
由于组件在其命名空间自己的虚拟环境中运行,Lightning Web Security 可以通过修改 JavaScript API 级别的代码来防止不安全的行为。修改或扭曲将应用于沙箱中。
Lightning Web Security 以最新的 TC39 标准为蓝本,这些标准随着浏览器平台的发展而发展。
下表比较了 Lightning Locker 功能与 Lightning Web Security。
特征
闪电储物柜
Lightning Web 安全
JavaScript 严格模式
执行
执行
DOM 访问遏制
组件只能访问它们创建的元素。请参阅 DOM 访问遏制。Lightning Locker 可防止您破坏 Lightning Web 组件之间的 Shadow DOM 封装。您无法修改属性。shadowRoot
组件对 DOM 的访问由浏览器通过 Shadow DOM 控制,Shadow DOM 是在 Web 浏览器中实现的 Web 标准。Lightning Web Security 通过要求 to be 强制执行 closed,这提供了额外的封装限制。参见 MDN Web 文档:ShadowRoot.mode。shadowRootmodeclosed
Salesforce 建议将 Visual Studio Code 与 Salesforce Extensions for Visual Studio Code 结合使用来开发 Lightning Web 组件。针对 Lightning Web Security 的错误的 ESLint 规则显示在 VS Code 中,您的代码违反了这些规则。这些规则映射到影响代码的扭曲。规则冲突的弹出窗口包括指向规则文档的链接。
在这里,您可以看到 lint 规则冲突的弹出窗口,以及指向相关规则文档的链接。
此弹出窗口显示 lint 警告,而不是错误。
针对项目运行基本规则
安装 ESLint 规则和配置,如添加 ESLint 对 Lightning Web 安全的支持部分所述。
运行常用命令以对代码进行 lint 检查。如果您使用的是 Salesforce DX 项目,请运行适合您环境的命令。npmyarn$ npm run lint或$ yarn lint如果您使用的不是 Salesforce DX 项目,则可以在 LWC 组件上运行该命令。例如,如果您的组件包含在文件夹中:eslintlwc$ eslint **/lwc/**无论哪种情况,输出都类似于此示例。10:16 error Document#fullscreen is prohibited in Lightning Locker @locker/locker/distorted-document-blocked-properties ✖ 1 problem (1 error, 0 warnings) error Command failed with exit code 1.
在此示例中,在命名空间 A 中定义,在命名空间 B 中定义。redFunctionblueFunction
//module in namespace A
function redFunction(arg) {
arg.foo = "bar"; // mutation on the arg object
console.log(arg.foo); // "bar"
}
//module in namespace B
function blueFunction() {
var someObject = {};
redFunction(someObject); // blueFunction expects foo property added by redFunction
console.log(someObject.foo); // undefined
}
当 uses 时,它不会接收在命名空间 A 中完成的属性的突变。命名空间可以通过相互传递数据进行通信,但突变不会持续到命名空间边界之外。blueFunctionredFunctionfoo