Salesforce跨组件通信(Lightning Component + Lightning Web Component) LMS应用

345 阅读1分钟

跨组件之间的通信

Lightning 消息服务在 Salesforce 的 Winter '20 版本中引入,是开箱即用的功能,使您能够在 Visualforce 和 Lightning 组件(包括 Aura Web 组件 (AWC) 和 Lightning Web 组件 (LWC))之间进行通信。

Lightning 消息服务基于 Lightning 消息通道,一种新的元数据类型。 Lightning Message Channel 用于通过作用域模块@salesforce/messageChannel 访问 LWC 中的 Lightning Message Service API 和 Lightning Message Channel。 对于 Visualforce,您可以使用全局变量 $MessageChannel 对于 Aura 中,您可以在组件中使用 lightning:messageChannel

闪电消息服务的使用

  1. 要启用 Visualforce 页面、Lightning Web 组件和 Aura 组件之间的通信

  2. 访问 Lightning 消息服务 API 以在整个 Lightning Experience 中发布消息。此外,它还有助于通过 Lightning 消息通道订阅来自 Lightning Experience 中任何地方的消息。 

  3. 特定命名空间与 Lightning 消息通道相关联。开发者可以选择他们的消息通道是否对其他命名空间可用,以确保闪电消息服务上的通信安全。

效果展示:

Dec-01-2022 10-40-17.gif

关系图

两个组件之间没有任何关联 12241669861281_.pic.jpg

我们在我们的组织中在哪里建立 Lightning 消息通道?

  1. 使用 VsCode,然后使用 Salesforce CLI 和 SFDX 项目。
  2. 在“force-app\main\default\”中创建文件夹“messageChannels”。
  3. 在“messageChanel”目录中 .messageChannel-meta.xml 例如,SampleMessageChannel.messageChannel-meta.xml。
  4. 将消息通道部署到您的组织以了解您的消息通道,然后在您的代码中使用它。
  5. 另一个命名空间中的消息通道,可以与命名空间后跟两个下划线一起使用。因此,如果 SampleMessageChannel 来自 example 命名空间则是example__SampleMessageChannel__c。
<!-- 代表与Lightning消息通道相关的元数据。Lightning消息通道代表了一个安全通道,用于跨UI技术
(Lightning Web组件、Aura组件和Visualforce)的通信.该类型扩展了Metadata元数据类型,并继
承了它的fullName字段。-->

<?xml version="1.0" encoding="UTF-8"?>
<LightningMessageChannel xmlns="http://soap.sforce.com/2006/04/metadata">
     <!-- masterLabel 必填-->
     <masterLabel>SampleMessageChannel</masterLabel>
     <!-- Exposed 是可选的布尔字段,如果您不指定它,它会声明为 false 此外,该字段告诉我们的系统特定消息通道是否可用于其他名称空间中的组件。 -->
     <isExposed>true</isExposed>
     <description>This is a sample Lightning Message Channel.</description>
    
     <!-- LightningMessageField代表一个给定的Lightning消息通道的消息有效载荷字段 -->
     <lightningMessageFields>
        <fieldName>recordId</fieldName>
        <description>This is the record Id that changed</description>
     </lightningMessageFields>
     <lightningMessageFields>
        <fieldName>recordData</fieldName>
      <description>The current data representing the record that changed</description>
    </lightningMessageFields>
</LightningMessageChannel>

参考

developer.salesforce.com/docs/atlas.…

<!-- package.xml -->
<?xml version="1.0" encoding="UTF-8"?>
<Package xmlns="http://soap.sforce.com/2006/04/metadata">
    <types>
        <members>*</members>
        <name>LightningMessageChannel</name>
    </types>
    <version>47.0</version>
</Package>

ContactListController控制器类

public class ContactListController {
	
    @AuraEnabled
    public static List<Contact> getContactList(String accountId){
        return [SELECT Id,Name,Email FROM Contact WHERE AccountId = :accountId LIMIT 10];
    }
    
    @AuraEnabled
    public static Contact insertContact(String accountId,Contact contact){
        contact.AccountId = accountId;
        insert contact;
        return contact;
    }
}

QuickCreatedContact.cmp

<aura:component implements="flexipage:availableForRecordHome,force:hasRecordId" controller="ContactListController" access="global" >
 	
 
    <!--QuickCreatedContact-->
    <aura:attribute name="insertContact" type="Contact" access="public" 
                    default="{FirstName:'',
                              LastName:'' ,
                              Email:'' ,
                              Phone:'' }"/>
    
    <aura:attribute name="title" type="String" default="Quick Create Contact"/>
    
    <!-- 要从应用程序的任何地方接收消息通道上的消息,请使用lightning:messageChannel的可选参数范围。将范围设置为 "APPLICATION"值。 -->
    <lightning:messageChannel type="sampleMessageChannel__c"
                              scope="APPLICATION"
                              aura:id="sampleMessageChannel"/>
    
    <div>
        <lightning:card >
            <aura:set attribute="title">
                <lightning:icon iconName="utility:connected_apps" size="small"/>
                {!v.title}
            </aura:set>
            <div style="padding:0px 10px">
                <lightning:input aura:id="field" name="input1" required="true" fieldLevelHelp="The event name must be less than 50 characters" label="First Name" messageWhenValueMissing="FirstName Missing" value="{!v.insertContact.FirstName}" placeholder="type here..." type="text"/>

                <lightning:input aura:id="field" name="input2" required="true" label="Last Name" messageWhenValueMissing="LastName Missing" value="{!v.insertContact.LastName}" placeholder="type here..." type="text"/>

                <lightning:input aura:id="field" name="input3" required="true" label="Email" messageWhenValueMissing="Email Missing" value="{!v.insertContact.Email}" placeholder="type here..." type="email"/>

                <lightning:input aura:id="field" name="input4" required="true" label="Phone" messageWhenValueMissing="Phone Missing" value="{!v.insertContact.Phone}" placeholder="type here..." type="text" />
                <br/>
                <lightning:button variant="brand-outline" label="Create Contact" title="Create Contact" onclick="{!c.createContact}" />
             </div>   
            </lightning:card>
    </div>
</aura:component>

QuickCreatedContactController

({
	createContact : function(component, event, helper) {
        // 获取记录
        let contact = component.get("v.insertContact");
        console.log(component.find('field'))
        // 验证数据的规范
        var allValid = component.find('field').reduce(function (validSoFar, inputCmp) {
            console.log(validSoFar)
            console.log(inputCmp)
            inputCmp.reportValidity(); // 报告验证的结果
            return validSoFar && inputCmp.checkValidity();
        }, true);
        if (allValid) {
            console.log('All form entries look valid. Ready to submit!');
        } else {
            console.log('Please update the invalid form entries and try again.');
        }
        let params = {
            accountId : component.get("v.recordId"),
            contact:contact
        }
       	var action = component.get("c.insertContact");
        action.setParams(params);
        // Create a callback that is executed after 
        // the server-side action returns
        action.setCallback(this, function(response) {
            var state = response.getState();
           
            contact = response.getReturnValue();
            // 你通常会在这里启动一个事件,以触发客户端的通知,即服务器端的行动已经完成。
            if (state === "SUCCESS") {
                 console.log("contact",contact)
                // 传递的数据
                var payload = {
                    recordId: component.get("v.recordId"),
                    recordData: {
                        value: contact
                    }
                };
            
                component.find("sampleMessageChannel").publish(payload);
                component.set("v.insertContact",{});
                
                
            }else if (state === "INCOMPLETE") {
                // do something
            }else if (state === "ERROR") {
                var errors = response.getError();
                if (errors) {
                    if (errors[0] && errors[0].message) {
                        console.log("Error message: " + errors[0].message);
                    }
                } else {
                    console.log("Unknown error");
                }
            }
        });
        // 一个客户端动作可能引起多个事件,这些事件可能触发其他事件和其他服务器端动作调用。$A.enqueueAction将服务器端的动作添加到队列中。
            $A.enqueueAction(action);
	},
        
   register : function(component, event) {
 		var inputCmp = component.find("inputCmp");
        var value = inputCmp.get("v.value");
        // is input valid text?
        if (value === "John Doe") {
            inputCmp.setCustomValidity("John Doe is already registered"); 
        } else {
            inputCmp.setCustomValidity(""); // 如果之前有一个自定义错误,请重置它
        }
        inputCmp.reportValidity(); // 告诉lightning:input立即显示错误,不需要交互
    },
    
    publishMC: function(cmp, event, helper) {
        // 传递的数据到订阅信道的组件
        var payload = {
            recordId: cmp.get("v.recordId"),
            recordData: {
                value: component.get("v.insertContact")
            }
        };
       // 发布信息
        cmp.find("sampleMessageChannel").publish(payload);
    }
})

ContactListCom.cmp

<!-- controller="ContactListController" 指定控制器类 -->
<aura:component controller="ContactListController" implements="force:hasRecordId,flexipage:availableForAllPageTypes">
        <!--引用 sampleMessageChannel__c 信道并指定监听事件 -->
	<lightning:messageChannel type="sampleMessageChannel__c" scope="APPLICATION" onMessage="{!c.handleChanged}"/>
    
    <!-- 组件初始化时执行的事件 -->
	<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
    <aura:attribute name="ContactList" type="List" access="public"/>
	
    <div>
        <lightning:card >
            <aura:set attribute="title">
            <lightning:icon iconName="standard:account" alternativeText="Account" title="Account" />
            最近联系人:
        </aura:set>
        <!-- indexVar 是索引属性 var 是数组元素的临时变量 -->
       	<aura:iteration items="{!v.ContactList}" var="contact" indexVar="index">
       		<div style="display: flex;flex-direction: row;justify-content: space-around;border-bottom:1px solid #BABABA;padding:10px 5px; align-items: center;" >
            	<div style="width:calc(100% - 90px);">
                    联系人:<span>{!contact.Name}</span>
                    <div style="height:10px;width:100%"></div>
                    邮 箱:<span>{!contact.Email}</span>
                </div>
                <div style="width:90px;">
                    <!-- Brand variant: Identifies the primary action in a group of buttons --> 
                    <lightning:button variant="brand" name="{!contact.Id}" label="查看详情" title="查看详情" onclick="{!c.jumpToContact}"/>
                </div>
            </div>
    	</aura:iteration>
        </lightning:card>
    </div>
</aura:component>

ContactListController

({
	doInit : function(component, event, helper) {
	console.log('执行初始化!')
        // 调用命名空间内的方法
        let params = {
            accountId : component.get("v.recordId")
        }
        var action = component.get("c.getContactList"); // 调用控制器中的方法
        console.log(params);
        action.setParams(params);
        action.setCallback(this, function(response) {
            var state = response.getState();
            if (state === "SUCCESS") {
                // response.getReturnValue()调用的返回值
                component.set("v.ContactList", response.getReturnValue()); 
            }
         });
         $A.enqueueAction(action); //加入调用队列
	},
    
    jumpToContact :function(component, event, helper) {
        const jumpbutton = event.getSource();
        const contactId = jumpbutton.get('v.name');
	// 跳转到Contact页面 $A.get("e.force:navigateToSObject"); 
        var navEvt = $A.get("e.force:navigateToSObject");
        navEvt.setParams({
          "recordId": contactId,
        });
        navEvt.fire();
    },
    

    handleChanged: function(cmp, message, helper) { 
        // Read the message argument to get the values in the message payload
        console.log("监听到了........")
        if (message != null && message.getParam("recordData") != null) {
           let passContact = message.getParam("recordData").value;
            let contact = {
                Name: passContact.FirstName + passContact.LastName,
                Email : passContact.Email,
                Id : passContact.Id
            }
            let contactList = cmp.get("v.ContactList");
            contactList.push(contact);
            cmp.set("v.ContactList",contactList);
       }
    }
})
lightning:messageChannel 必须是 aura:component 的子元素

在自定义 Aura 组件中,lightning:messageChannel 必须是 aura:component 标记的直接子项。
它不能嵌套在 HTML 标记或其他组件中

参考

Lightning Component 中创建Message Channel

developer.salesforce.com/docs/atlas.…

Lightning Component 中的JavaScript API

developer.salesforce.com/docs/atlas.…

Lightning Web Component 中创建Message Channel

developer.salesforce.com/docs/compon…

Lightning Web Component 中使用案例

developer.salesforce.com/docs/compon…

案例参考

blog.cloudanalogy.com/everything-…

www.apexhours.com/lightning-m…