学习目标
- 创建可以从Lightning组件代码远程调用的Apex方法。
- 从Lightning组件调用远程方法。
- 使用回调函数异步处理服务器响应。
- 伸展目标:解释“c。”,“c:”和“c。”之间的区别。
服务器端控制器的概念



从Salesforce查询数据
我们将从阅读Salesforce数据开始,在费用应用程序启动时加载现有费用列表。
public with sharing class ExpensesController {
// 斯特恩讲关于这里什么都没有了
@AuraEnabled
public static List<Expense__c> getExpenses() {
return [SELECT Id, Name, Amount__c, Client__c, Date__c,
Reimbursed__c, CreatedDate
FROM Expense__c];
}
}
- 方法声明之前的@AuraEnabled注解。 “Aura”是Lightning组件核心的开源框架的名称。您已经看到它在一些核心标签的命名空间中使用,如<aura:component>。现在你知道它来自哪里。
- 静态关键字。所有@AuraEnabled控制器方法都必须是静态方法,可以是公共范围或全局范围。
从Salesforce加载数据
下一步是将费用组件连接到服务器端的Apex控制器。这很容易,可能会让你头晕目眩。将费用组件的开头<aura:component>标记更改为指向Apex控制器,如下所示。
<aura:component controller="ExpensesController">
但是,指向Apex控制器并不实际加载任何数据,或者调用远程方法。就像组件和(客户端)控制器之间的自动连线一样,这个指向只是让这两个组件“彼此了解”。这个“知道”甚至采取了同样的形式,另一个价值提供者,我们稍后会看到。但是自动布线只有这么远。完成电路仍然是我们的工作。
在这种情况下,完成电路意味着以下。
- 当费用组件被加载时,
- 查询Salesforce的现有费用记录,和
- 将这些记录添加到费用组件属性。
<aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
调用服务器端控制器方法
一步下去,两步走。剩下的步骤都在doInit动作处理器中进行,所以让我们来看看它。将以下代码添加到费用组件的控制器。
// 从Salesforce中加载费用
doInit: function(component, event, helper) {
// 创建动作
var action = component.get("c.getExpenses");
// 添加回复行为的时候收到响应
action.setCallback(this, function(response) {
var state = response.getState();
if (state === "SUCCESS") {
component.set("v.expenses", response.getReturnValue());
}
else {
console.log("Failed with state: " + state);
}
});
// 发送动作执行
$A.enqueueAction(action);
},
- 创建一个远程方法调用。
- 设置远程方法调用返回时应该发生的事情。
- 排队远程方法调用。
var action = component.get("c.getExpenses");
|
识别 |
上下文 |
含义 |
|---|---|---|
|
c. |
组件标记 |
客户端控制器 |
|
c. |
控制器代码 |
服务器端控制器 |
|
c: |
标记 |
默认命名空间 |
好的,回到我们的代码。在我们偏离之前,我们正在看这条线。
var action = component.get("c.getExpenses");
$A.enqueueAction(action);
- 它将服务器请求排队。
- 就你的管制员行动而言,这是结束了。
- 你不能保证什么时候,或者如果,你会听到回来。
这是我们搁置的代码块的地方。但在我们谈论这个之前,有一点流行文化。
服务器调用,异步执行和回调函数
doInit: function(component, event, helper) {
// 从Salesforce中加载费用
var action = component.get("c.getExpenses");
action.setCallback(
// 这是我的号码,也许打电话给我
);
$A.enqueueAction(action);
},
action.setCallback(this, function(response) { ... });
action.setCallback(scope, callbackFunction);
// 不是真正的代码!不要剪贴!
doInit: function(component, event, helper) {
// 创建服务器请求
var action = component.get("c.getExpenses");
// 发送服务器请求
$A.enqueueAction(action);
// ... 时间流逝 ...
// ...
// ...危险主题扮演...
// ...
// ...在不确定的未来某个时候
// 处理服务器响应
var state = action.response.getState();
if (state === "SUCCESS") {
component.set("v.expenses", action.response.getReturnValue());
}
},
处理服务器响应
现在我们已经得到了创建一个服务器请求的结构,让我们来看看我们的回调函数实际处理响应的细节。这里只是回调函数。
function(response) {
var state = response.getState();
if (state === "SUCCESS") {
component.set("v.expenses", response.getReturnValue());
}
}
在这个特定的回调函数中,我们执行以下操作。
- 获取响应的状态。
- 如果状态是成功的,也就是说,我们的要求按计划完成,那么:
- 将组件的费用属性设置为响应数据的值。
你可能有几个问题,比如:
- 如果响应状态不是成功会发生什么?
- 如果回应永远不会到来会发生什么? (打电话给我,可能的话。)
- 我们如何才能将响应数据分配给我们的组件属性?
前两个答案不幸的是,我们不打算在这个模块中介绍这些可能性。他们当然是你需要知道的事情,并考虑在你的真实世界的应用程序,但我们只是没有空间。
最后一个问题在这里是最相关的,但也是最容易回答的。我们为费用属性定义了一个数据类型。
<aura:attribute name="expenses" type="Expense__c[]"/>
public static List<Expense__c> getExpenses() { ... }
Apex 控制器的闪电组件
在我们开发应用程序的下一步之前,让我们深入一点Apex控制器。下面看看下一个版本,我们需要处理创建新记录,以及更新报销?复选框在现有的记录。
public with sharing class ExpensesController {
@AuraEnabled
public static List<Expense__c> getExpenses() {
// 先执行isAccessible()检查,然后
return [SELECT Id, Name, Amount__c, Client__c, Date__c,
Reimbursed__c, CreatedDate
FROM Expense__c];
}
@AuraEnabled
public static Expense__c saveExpense(Expense__c expense) {
// 首先执行isUpdatable()检查
upsert expense;
return expense;
}
}
@AuraEnabled
public static List<Expense__c> getExpenses() {
// 检查以确保所有的字段都可以被这个用户访问
String[] fieldsToCheck = new String[] {
'Id', 'Name', 'Amount__c', 'Client__c', 'Date__c',
'Reimbursed__c', 'CreatedDate'
};
Map<String,Schema.SObjectField> fieldDescribeTokens =
Schema.SObjectType.Expense__c.fields.getMap();
for(String field : fieldsToCheck) {
if( ! fieldDescribeTokens.get(field).getDescribe().isAccessible()) {
throw new System.NoAccessException();
return null;
}
}
// 好,他们很酷,让他们通过
return [SELECT Id, Name, Amount__c, Client__c, Date__c,
Reimbursed__c, CreatedDate
FROM Expense__c];
}
好, </stern-lecture>.
将数据保存到Salesforce
createExpense: function(component, expense) {
var action = component.get("c.saveExpense");
action.setParams({
"expense": expense
});
action.setCallback(this, function(response){
var state = response.getState();
if (state === "SUCCESS") {
var expenses = component.get("v.expenses");
expenses.push(response.getReturnValue());
component.set("v.expenses", expenses);
}
});
$A.enqueueAction(action);
},
需要注意的事项
<lightning:input aura:id="expenseform"
label="Expense Name"
name="expensename"
value="{!v.newExpense.Name}"
required="true"/>
var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
// 显示无效字段的错误消息
inputCmp.showHelpMessageIfInvalid();
return validSoFar && inputCmp.get('v.validity').valid;
}, true);