Salesforce Lightning Component通过Lightning Out暴露给外部服务
【前言】:在实施Service Cloud Embeded Service时候,自定义Pre-Chat,Chat WIndow等需要用到Lightning Component,所以之前使用VFP + Site这一套方案实施的Live Agent需要升级为Lightning Component + Embeded Service + Site了,那么第一步需要解决的是如何将Lightning Component通过Site暴露给外部服务?经查阅资料,发现Lightning Out可以在VFP中引用Lightning Component。本篇将以Demo形式展示Lightning Out这一技术。
【应用场景举例】:
1、使用Aura Component写了Trailhead排名应用,想暴露给参与者;
2、通过git应用托管服务,将sf资源暴露给end user,Sample;
【技术骨架】:
【Demo演练】:
目标:我们有2个Aura Component,分别用来展示10个Accounts和Contacts,我们将2个组件放入同一个App里面,然后通过Lightning Out将组件内容渲染到VFP指定的块,最终通过Site将VFP暴露给外部(免登陆访问SF资源)。
效果:
代码示例:
Lightning Component部分:
a. Component & Controller.js
<!-- DisplayTenAccountsComp.cmp -->
<aura:component controller="LightningOutCtrl">
<aura:attribute name="accList" type="Account[]" access="private"/>
<aura:attribute name="userInfo" type="String" access="private"/>
<aura:attribute name="conList" type="Contact[]" access="private"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<div>
<p>Current UserInfo: {!v.userInfo}</p>
</div>
<br/>
<ul>
<aura:iteration items="{!v.accList}" var="acc">
<li>{!acc.Id} · {!acc.Name}</li>
</aura:iteration>
</ul>
<br/>
<ul>
<aura:iteration items="{!v.conList}" var="con">
<li>{!con.Id} · {!con.Name} · {!con.Account.Name}</li>
</aura:iteration>
</ul>
</aura:component>
<!-- DisplayTenAccountsCompController.js -->
({
doInit : function(component, event, helper) {
let action = component.get("c.getTenAccounts");
action.setCallback(this, function(response) {
const data = response.getReturnValue();
const dataSize = data.length;
console.log('dataSize: ' + dataSize);
component.set('v.accList', data);
});
$A.enqueueAction(action);
let action1 = component.get("c.getTenContacts");
action1.setCallback(this, function(response) {
const data = response.getReturnValue();
const dataSize = data.length;
console.log('dataSize: ' + dataSize);
component.set('v.conList', data);
});
$A.enqueueAction(action1);
let action2 = component.get("c.getUserInfo");
action2.setCallback(this, function(response) {
const data = response.getReturnValue();
component.set('v.userInfo', data);
});
$A.enqueueAction(action2);
}
})
<!-- DisplayTenContactsComp.cmp -->
<aura:component controller="LightningOutCtrl">
<aura:attribute name="conList" type="Contact[]" access="private"/>
<aura:handler name="init" value="{!this}" action="{!c.doInit}"/>
<ul>
<aura:iteration items="{!v.conList}" var="con">
<li>{!con.Id} · {!con.Name}</li>
</aura:iteration>
</ul>
</aura:component>
<!-- DisplayTenContactsCompController.js -->
({
doInit : function(component, event, helper) {
let action1 = component.get("c.getTenContacts");
action1.setCallback(this, function(response) {
const data = response.getReturnValue();
const dataSize = data.length;
console.log('dataSize: ' + dataSize);
component.set('v.conList', data);
});
$A.enqueueAction(action1);
}
})
b. Apex Controller
public without sharing class LightningOutCtrl {
@AuraEnabled
public static List<Account> getTenAccounts() {
return [SELECT Id, Name FROM Account LIMIT 10];
}
@AuraEnabled
public static List<Contact> getTenContacts() {
return [SELECT Id, Name, Account.Name FROM Contact LIMIT 10];
}
@AuraEnabled
public static String getUserInfo() {
return '{"UserName": '+UserInfo.getUserName()+', "Name": '+UserInfo.getName()+', "Email": '+UserInfo.getUserEmail()+'}';
// return [SELECT Id, Name, UserName, Email, EmailEncodingKey, Profile.Name, UserRole.Name FROM User WHERE Id =:UserInfo.getUserId()];
}
}
c. Dependency App(此处实现了ltng:allowGuestAccess接口用于暴露服务给外部用户,若未声明需要通过配置Connected App,然后再第三方应用通过账密/token登录后访问SF资源)
<!-- ALightningOutApp.app -->
<aura:application extends="ltng:outApp" access="global" implements="ltng:allowGuestAccess">
<aura:dependency resource="c:DisplayTenAccountsComp" />
<aura:dependency resource="c:DisplayTenContactsComp" />
</aura:application>
d. VFP
<!-- Component和VFP不同源 -->
<apex:page showHeader="false" sidebar="false">
<script src="https://yoursitedomain.force.com/site/lightning/lightning.out.js"></script>
<center><apex:outputText style="font-size: 18px; color: #f0f" value="Hello, this is my Lightning Out Demo" /></center>
<div id='accList'></div>
<br/>
<div id='conList'></div>
<script>
// dependencyApp
$Lightning.use('c:ALightningOutApp', function() {
// component used in dependencyApp
$Lightning.createComponent('c:DisplayTenAccountsComp',
{},
'accList',
function() {
//do some stuff
console.log('accList has been created...');
});
// component used in dependencyApp
$Lightning.createComponent('c:DisplayTenContactsComp',
{},
'conList',
function() {
//do some stuff
console.log('conList has been created...');
});
}, 'https://yoursitedomain.force.com/site'
);
</script>
</apex:page>
<!-- Component和VFP同源 -->
<apex:page showHeader="false" sidebar="false">
<apex:includeLightning />
<center><apex:outputText style="font-size: 18px; color: #f0f" value="Hello, this is my Lightning Out Demo" /></center>
<div id='accList'></div>
<br/>
<div id='conList'></div>
<script>
$Lightning.use('c:ALightningOutApp', function() {
// component used in dependencyApp
$Lightning.createComponent('c:DisplayTenAccountsComp',
{},
'accList',
function() {
//do some stuff
console.log('accList has been created...');
});
// component used in dependencyApp
$Lightning.createComponent('c:DisplayTenContactsComp',
{},
'conList',
function() {
//do some stuff
console.log('conList has been created...');
});
});
</script>
</apex:page>
【特别注意】:
1. 如果不同源,需要在Comonent所在的Org加CORS(同源不存在跨域,不需要加);
2. 必须在site profile里启用vfp(同源时),apex和OLS(缺一不可);
3. 如果查询org级别数据,apex必须使用without sharing,FLS加不加无所谓;
否则console出现服务器500错误或如下错误:
Access to XMLHttpRequest at 'https://yoursitedomain1.force.com/site1/ALightningOutApp.app?aura.format=JSON&aura.formatAdapter=LIGHTNING_OUT' from origin 'https://yoursitedomain2.force.com' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.