学习目标
- 创建Lightning组件并根据用户输入填充字段,并向用户提供反馈消息。
- 创建与第三方系统集成的Lightning组件和操作。
- 开始将您的JavaScript自定义按钮功能迁移到适合Lightning的替代方案。
闪电行动:智能,快速和移动
我们已经介绍了在Lightning Experience和Salesforce Classic中都可以使用的几种解决方案,这些解决方案是JavaScript按钮的绝佳替代品。但是我们认识到,这些声明性和程序性解决方案并不涉及每个用例。好消息是有更多的东西可以解决。引入闪电行动:称为闪电组件的快速行动。
闪电行动是建立在你已经采用的现有闪电组件框架之上的。您可以轻松地将您现有的Lightning组件转换为操作,并在Salesforce应用程序和Lightning Experience中使用它们。
通过将两个接口之一添加到组件,您可以将Lightning组件作为操作进行调用:force:lightningQuickAction或force:lightningQuickActionWithoutHeader。第一个界面添加标准的标题与保存和取消按钮闪电动作覆盖,而另一个不。
另一个有用的Lightning动作接口是force:hasRecordId,它从记录页面调用时向组件提供记录上下文。如果您将Lightning操作设置为全局快速操作,则不需要记录上下文。但是,如果要访问记录的数据或元数据,则必须实施force:hasRecordId。
注意
如果您之前没有构建Lightning组件,请访问Lightning开发中心,执行Lightning组件快速启动项目,并完成Lightning组件基础知识模块。
让我们深入闪电行动。接下来,我们将讨论一些可以用Lightning操作来解决的JavaScript按钮功能。
- 根据用户输入填充字段并在数据输入期间提供反馈消息
- 集成第三方API
根据用户输入填充字段并向用户提供反馈消息
假设您使用JavaScript按钮来验证或操作数据,并在用户使用记录时为用户提供反馈或说明。下面是一个例子,说明在用户创建或更新记录之前,您可以轻松地验证或操作Lightning组件中的数据。
我们在我们的示例组织中构建了一个名为“案例研究”的自定义对象,我们用它来跟踪不同的研究项目。当我们添加新的测试用户时,我们会捕获他们的姓名和电子邮件地址由于电子邮件地址是我们的主要联系方式,因此我们要确保输入正确。我们也想为测试用户创建一个独特的昵称,以便他们保持匿名。
我们将创建一个闪电操作,显示名称和电子邮件地址字段,验证电子邮件地址,并使用名字和随机数自动创建昵称。
这是Lightning操作调用的组件的代码。
- CreateUser.cmp – 打开操作时看到的Lightning组件。它包含UI实现,其中包括文本字段,按钮,动作标题等。
- CreateUserController.js – 监听组件以及发生的所有UI事件(如初始加载,文本输入和按钮单击)的控制器。它的功能是将这些事件委托给辅助类CreateUserHelper.js。
- CreateUserHelper.js – 处理所有业务逻辑的代码,例如验证密码和电子邮件字段,以及与保存数据的服务器端Apex控制器进行通信。
- SaveTestUser.apxc – 处理创建测试用户的请求的简单的Apex控制器。
CreateUser.cmp
<aura:component implements="force:lightningQuickActionWithoutHeader,force:hasRecordId" controller="SaveTestUser" >
<aura:attribute name="user" type="Test_User__c" default="{ 'sobjectType': 'Test_User__c' }"/>
<aura:attribute name="hasErrors" type="Boolean" description="Indicate whether there were failures or not" />
<aura:attribute name="caseStudy" type="String" />
<aura:attribute name="recordId" type="String"/>
<aura:handler name="init" value="{!this}" action="{!c.init}" />
<div class="slds-page-header" role="banner">
<p class="slds-text-heading--label">Case Study</p>
<h1 class="slds-page-header__title slds-m-right--small slds-truncate slds-align-left" title="Case Study Title">{!v.caseStudy}</h1>
</div>
<br/>
<aura:if isTrue="{!v.hasErrors}">
<!-- Load error -->
<div class="userCreateError">
<ui:message title="Error" severity="error" closable="true">
Please review the error messages.
</ui:message>
</div>
</aura:if>
<div class="slds-form--stacked">
<div class="slds-form-element">
<label class="slds-form-element__label" for="firstName">Enter first name: </label>
<div class="slds-form-element__control">
<ui:inputText class="slds-input" aura:id="firstName" value="{!v.user.first}" required="true" keydown="{!c.updateNickname}" updateOn="keydown"/>
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="lastName">Enter last name: </label>
<div class="slds-form-element__control">
<ui:inputText class="slds-input" aura:id="lastName" value="{!v.user.last}" required="true" />
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="nickname">Enter nickname: </label>
<div class="slds-form-element__control">
<ui:inputText class="slds-input" aura:id="nickname" value="{!v.user.nickname}" required="false"/>
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="userEmail">Enter user's email:</label>
<div class="slds-form-element__control">
<ui:inputEmail class="slds-input" aura:id="userEmail" value="{!v.user.Email__c}" required="true"/>
</div>
</div>
<div class="slds-form-element">
<label class="slds-form-element__label" for="userPassword">Enter user's password:</label>
<div class="slds-form-element__control">
<ui:inputSecret class="slds-input" aura:id="userPassword" value="{!v.user.Password__c}" required="true"/>
</div>
</div>
<div class="slds-form-element">
<ui:button class="slds-button slds-button--neutral" press="{!c.cancel}" label="Cancel" />
<ui:button class="slds-button slds-button--brand" press="{!c.saveUserForm}" label="Save User" />
</div>
</div>
</aura:component>
({
init : function(component, event, helper) {
var action = component.get("c.getCaseStudy");
action.setParams({"recordId": component.get("v.recordId")});
action.setCallback(this, function(response) {
var state = response.getState();
if(component.isValid() && state == "SUCCESS"){
component.set("v.caseStudy", response.getReturnValue());
} else {
console.log('There was a problem and the state is: '+state);
}
});
$A.enqueueAction(action);
},
updateNickname: function(component, event, helper) {
// Update the nickname field when 'tab' is pressed
if (event.getParams().keyCode == 9) {
var nameInput = component.find("firstName");
var nameValue = nameInput.get("v.value");
var nickName = component.find("nickname");
var today = new Date();
nickName.set("v.value", nameValue + today.valueOf(today));
}
},
saveUserForm : function(component, event, helper) {
var name = component.get("v.user.first");
var last = component.get("v.user.last");
var password = component.get("v.user.Password__c");
var email = component.get("v.user.Email__c");
var nickname = component.get("v.user.nickname");
var passwordCmp = component.find("userPassword");
var emailCmp = component.find("userEmail");
helper.validatePassword(component, event, helper);
helper.validateEmail(component, event, helper);
if (passwordCmp.get("v.errors") == null && emailCmp.get("v.errors") == null) {
component.set("v.hasErrors", false);
helper.save(component,name + " " + last,password,email,nickname);
} else {
component.set("v.hasErrors", true);
}
},
cancel : function(component, event, helper) {
$A.get("e.force:closeQuickAction").fire();
}
})
({
save: function(component, name, password, email, nickname) {
//Save the user and close the panel
var action = component.get("c.saveUser");
action.setParams({
"name": name,
"password": password,
"email": email,
"nickname": nickname,
"caseStudy": component.get("v.recordId")
});
action.setCallback(this, function(a) {
var response = a.getReturnValue();
var state = action.getState();
if(component.isValid() && state == "SUCCESS"){
$A.get("e.force:closeQuickAction").fire();
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
"title": "Success!",
"message": "The test user has been created."
});
toastEvent.fire();
$A.get('e.force:refreshView').fire();
} else if (state == "ERROR") {
console.log('There was a problem and the state is: '+ action.getState());
}
});
$A.enqueueAction(action);
},
validatePassword : function(component, event, helper) {
var inputCmp = component.find("userPassword");
var value = inputCmp.get("v.value");
if (value == undefined) {
inputCmp.set("v.errors", [{message: "You must enter a password."}]);
} else if (value.length < 7 || value.length > 15) {
inputCmp.set("v.errors", [{message: "The password is the wrong length: " + value}]);
} else if (value.search(/[0-9]+/) == -1) {
inputCmp.set("v.errors", [{message: "The password must contain at least one number: " + value}]);
} else if (value.search(/[a-zA-Z]+/) == -1) {
inputCmp.set("v.errors", [{message: "The password must contain at least one letter: " + value}]);
} else {
inputCmp.set("v.errors", null);
}
},
validateEmail : function(component, event, helper) {
var inputCmp = component.find("userEmail");
var value = inputCmp.get("v.value");
if (value == undefined) {
inputCmp.set("v.errors", [{message: "You must enter an email."}]);
return;
}
var apos = value.indexOf("@");
var dotpos = value.lastIndexOf(".");
if (apos<1||dotpos-apos<2){
inputCmp.set("v.errors", [{message: "Email is not in the correct format: " + value}]);
} else if (value.substring(apos+1, dotpos) != "gmail") {
inputCmp.set("v.errors", [{message: "Email must be a gmail account: " + value.substring(apos+1, dotpos)}]);
} else {
inputCmp.set("v.errors", null);
}
}
})
public class SaveTestUser {
@AuraEnabled
public static Test_User__c saveUser(String name, String password, String email, String caseStudy, String nickname) {
Test_User__c testUser = new Test_User__c(Name=name, Password__c=password, Email__c=email, Nickname__c=nickname, Case_Study__c=caseStudy);
upsert testUser;
return testUser;
}
@AuraEnabled
public static String getCaseStudy(String recordId) {
Case_Study__c caseStudyInstance = [SELECT Name FROM Case_Study__c WHERE id=:recordId];
return caseStudyInstance.Name;
}
}
在创建Lightning组件之后,我们将其分配给一个动作。在案例研究的对象管理设置中,我们转到“按钮,链接和操作”,单击“新建操作”,然后使用这些参数配置操作。
字段 | 值Value |
---|---|
Object Name | Case Study |
Action Type | Lightning Component |
Lightning Component | c:CreateUser |
Height | 500px |
Label | Create Test User |
Name | CreateUser |
然后,我们将新的Lightning操作添加到案例研究页面布局。当用户从案例研究记录页面调用它时,他们会看到我们创建的Lightning操作。
关于这个闪电行动的好处是,它也可以在Salesforce应用程序中运行。
集成第三方API
也许你使用JavaScript按钮来与第三方系统集成。你可以使用闪电行动吗?你当然可以。主要区别在于,使用Lightning操作进行集成时,必须使用服务器端控制器。作为回报,您可以更好地处理安全证书,并能够使用Apex进行异步和批处理API调用。
我们来看看如何与Twilio集成来发送短信。在这个例子中,我们在奢侈旅行业务,我们处理名人和贵宾。我们希望我们的客户服务代理能够与客户沟通,但我们不希望将客户的个人联系信息暴露给任何人。我们在Contact对象上创建一个Lightning动作,这样代理可以在不看到联系人的电话号码的情况下发送消息。
就像在我们的案例研究中,我们将创建Lightning组件和helper类,然后创建一个快速的操作来调用组件。
这个Lightning动作由Lightning组件,JavaScript控制器和Apex控制器组成,它们引用处理Twilio集成的库类。
SendTwilioSMS.cmp
<aura:component controller="TwilioSendSMSController" implements="flexipage:availableForAllPageTypes,force:hasRecordId,force:lightningQuickAction" >
<aura:attribute name="textMessage" type="String" />
<aura:attribute name="destinationNumber" type="String" />
<aura:attribute name="messageError" type="Boolean" />
<aura:attribute name="recordId" type="String" />
<aura:handler name="init" value="{!this}" action="{!c.init}" />
<aura:if isTrue="{!v.messageError}">
<!-- Load error -->
<div class="userCreateError">
<ui:message title="Error" severity="error" closable="true">
Unable to send message. Please review your data and try again.
</ui:message>
</div>
</aura:if>
<div class="slds-form--stacked">
<label class="slds-form-element__label" for="instructMsg">Please enter the message (max 160 char) below: </label>
<br/>
<div class="slds-form-element__control">
<ui:inputText class="slds-input" aura:id="message" label="Text Message" value="{!v.textMessage}" required="true" maxlength="160" size="165" />
</div>
<div class="centered">
<ui:button class="slds-button slds-button--brand" press="{!c.sendMessage}" label="Send Message"/>
</div>
</div>
</aura:component>
SendTwilioSmsController.js
({
init : function(component, event, helper) {
var action = component.get("c.getPhoneNumber");
action.setParams({"contactId": component.get("v.recordId")});
action.setCallback(this, function(response) {
var state = response.getState();
if(component.isValid() && state == "SUCCESS"){
component.set("v.destinationNumber", response.getReturnValue());
} else {
component.set("v.messageError", true);
}
});
$A.enqueueAction(action);
},
sendMessage : function(component, event, helper) {
var smsMessage = component.get("v.textMessage");
var number = component.get("v.destinationNumber");
var recordId = component.get("v.recordId")
var action = component.get("c.sendMessages");
action.setParams({"mobNumber": number, "message": smsMessage, "contactId": component.get("v.recordId")});
action.setCallback(this, function(response) {
var state = response.getState();
if(component.isValid() && state == "SUCCESS"){
$A.get("e.force:closeQuickAction").fire();
var toastEvent = $A.get("e.force:showToast");
toastEvent.setParams({
"title": "Success!",
"message": "SMS has been sent woo hoo!"
});
toastEvent.fire();
} else {
component.set("v.messageError", true);
}
});
$A.enqueueAction(action);
}
})
SendTwilioSmsController.apxc
/*
* Apex controller that currently contains only one method to send sms message
*/
global class TwilioSendSMSController {
/*
* This method uses the Twilio for Salesforce library class and method to
* send the message using the Twilio api.
*/
@AuraEnabled
webService static String sendMessages(String mobNumber, String message, Id contactId) {
System.debug('the mobNumber is: '+ mobNumber + ' and the message is: '+ message + ' and contactId is: ' + contactId);
if (mobNumber == null) {
mobNumber = getPhoneNumber(contactId);
}
try {
TwilioRestClient client = TwilioAPI.getDefaultClient();
Map<String,String> params = new Map<String,String> {
'To' => mobNumber,
'From' => '15555551234',
'Body' => message
};
TwilioSMS sms = client.getAccount().getSMSMessages().create(params);
return sms.getStatus();
} catch(exception ex) {
System.debug('oh no, it failed: '+ex);
return 'failed';
}
}
@AuraEnabled
public static String getPhoneNumber(Id contactId) {
Contact currentRecord = [SELECT Phone FROM Contact WHERE Id = :contactId];
return currentRecord.Phone.replace(' ', '').replace('-', '').replace(')', '').replace('(', '');
}
}
创建组件后,我们创建一个Lightning动作。这次我们将其添加到联系页面布局,以便代理可以访问它。
再一次,很酷的是,这个动作也可以在Salesforce应用程序中使用。如果汽车服务提供商试图在机场与客户联系,则驾驶员可以使用移动设备轻松地接触到客户。
闪电行动是闪电体验计划行动的未来。我们希望您开始将这套解决方案作为JavaScript按钮的更好替代方案。
如果您依赖于使用JavaScript按钮的合作伙伴应用程序,则很高兴知道我们的许多合作伙伴已经开始将其应用程序迁移并升级到Lightning。您可以在AppExchange上找到更新的Lightning Experience更多应用程序。
超越基础
如果您想自己尝试使用Twilio,则可以在Salesforce Github库中的第三方集成示例中找到用于Salesforce库类的Twilio的非托管包:https://github.com/twilio/twilio-销售队伍
在使用Twilio for Salesforce和Twilio API之前,请创建一个Twilio帐户,设置一个电话号码,并将您的帐户连接到Salesforce。有关更详细的说明,请参阅Github库中的TwilioApi.cls。