Lightning-组件(9)下一步

学习目标

完成本单元后,您将能够:

  • 列出可更深入探索的Lightning组件的五个方面。
  • 计划您可以对“费用”应用进行三项改进。
  • 赚。这个。徽章!

恭喜!

你做到了!我们为你感到兴奋,给你留下深刻的印象,并为你感到骄傲。严重的是,这是一个很难的模块,赚取这个徽章说了很多。恭喜!

这个单位的挑战就在于你和那个徽章之间的所有关系,毕竟你迄今为止所付出的努力,我们正在把这个简单化。但是,请不要跳过!虽然这个单位在挑战意义上是“容易的”,但是如果你想成为Lightning Components的开发者,那么这个单位就和所有其他单位一样重要。

Lightning组件基础模块教导了使用Lightning组件开发应用程序的基础知识,但这只是一个开始。只要这个模块已经到了这一步,实际上还有更多的东西要学习。至少,如果你想成为Lightning组件的主人。

在本单元中,我们将介绍两个主题。首先,我们将对这个模块中没有空间的事物进行调查,并提供可以开始了解这些事情的指针。之后,我们会建议您可以采取几个作业风格的项目。我们的小费用应用程序中有许多“显而易见”的补充,您可以尝试自己做。学习的最好方法就是在做!

什么我们掠过或压缩过去

当我们构建费用应用程序时,我们不得不说“我们不会在这里覆盖”这个次数超过了我们可以计算的次数。我们讨厌每一个人,你也可能也这样做。但是我们相信,如果我们把这个模块填充到八个小时以上,你会更加讨厌我们。

Salesforce闪电设计系统

SLDS是一个强大的,灵活的,全面的系统,在您的应用程序中实施Lightning Experience风格。您可以在Lightning组件,Visualforce中使用它,甚至在普通标记中使用它。 Salesforce文档小组的一位作者用它来创建…的原型,但是我们现在还不能告诉你。

SLDS是有趣的学习,甚至更有趣的使用。有一个专用于它的Trailhead模块(使用Visualforce)以及说明其用途的Trailhead项目的数量。还有一个哇哇网站专门把它放到人们的手中。

你可以在哪里使用闪电元件

我们以一系列的截图开始了这个话题,我们从来没有回头。但Lightning组件可以在Salesforce中以许多方式使用,甚至在其外部使用。

特别是,我们花了我们所有的时间来建立一个独立的“my.app”。您一定要学习如何将您的应用程序添加到Salesforce应用程序和闪电体验。好消息是,这很容易,这会让你想知道为什么我们没有掩盖它。 (请阅读,合作伙伴。)

“闪电组件开发人员指南”是关于在哪里以及如何使用Lightning组件应用程序的最佳信息来源。

调试

我们只涉及最原始的调试技术。学习调试你的Lightning组件应用程序,使用几种不同的复杂工具来做这件事,这将会带来好处,以你生命中的几个小时的形式回报,头发不会被拉出。

特别是,我们建议您学习Chrome的丰富DevTools套件以及在其中运行的Salesforce Lightning Inspector。 Apex开发人员也希望了解Apex提供的调试工具,其中许多工具直接位于开发者控制台中。

数据类型

我们简单地提到了一些可以用于属性的“特定于框架”的数据类型。主要是用于小平面,特别是身体的小平面。而且我们故意跳过了方面,特别是机身,因为它们很复杂,而且对于基本的Lightning Components开发来说并不是必不可少的。但是,最终你会想要了解所有这些概念,因为能够设置组件的主体(或其他方面)是一种强大的技术,可以为您节省大量的代码和尴尬。

助手

你在这个模块中写了一些相当有帮助的代码,但是还有一些帮助者的高级用法是值得了解的。由于助手是共享可重用代码的主要方式,因此这是一个重要的领域。

服务器请求生命周期和处理

我们介绍了如何使用$ A.enqueueAction()来创建服务器请求,以及如何处理成功的响应。还有其他的可能性,你真的需要知道如何处理它们。还有很多不同类型的请求,在某些情况下使用正确的请求可以显着提高应用程序的性能。

安全,安全,安全,安全

我们之前讲过,所以我们不会在这里。但是有很多东西需要知道。

应用程序事件

我们提到了这些,而且它们对于更大的应用程序很重要当我们介绍事件基础知识的时候,有很多东西需要了解。您无法使用Lightning组件构建复杂的应用程序,而无需了解所有事件。

我们什么都没有覆盖

还有一些我们没有提到的话题。其中有些是复杂的,有些是先进的,有些是两个。我们在这里列出一些您的考虑。

其他捆绑资源类型

我们介绍了四个核心Lightning组件捆绑资源类型。还有四个人。虽然设计和SVG资源有一些专门用途,但文档和渲染器资源在任何Lightning组件中都可能是有用的。

导航

你会被原谅,认为这是根本。在任何复杂的,真实世界的应用程序。在很多情况下,如果您正在使用Lightning Experience或Salesforce应用程序,开发导航实际上非常简单。但是,看下一个项目。

动态创建组件

在Lightning组件应用程序中“导航”的主要方式之一是动态创建新组件,以响应用户操作。这整个地区是丰富而强大的,而且足够复杂,你会在这个模块上一个星期的时间。我们会给你一个搜索词,你可以开始探索 $A.createComponent().

错误处理

有处理服务器错误,处理错误是纯粹的客户端。为了本单元的目的,我们假设你永远是成功的。可悲的是,在现实世界中,错误和怪异都以惊人的可靠性发生。伟大的工程师说得最好:任何可能出错的地方都会出错。所以你不妨尽可能地计划处理和从错误中恢复过来。

force:命名空间组件和事件

当您的Lightning组件应用程序在Lightning Experience和Salesforce应用程序中运行时,可以使用许多非常酷的组件和事件。其中一些组件在其他环境下工作,但其中许多组件在Lightning Experience和Salesforce应用程序中都很容易使用,所以我们没有看到它们。

系统事件

从技术上讲,我们以init处理程序的形式介绍了这一点,但是我们没有解释init事件是在组件或应用程序生命周期中可以捕获的一系列系统事件之一。 (对于这个问题,我们也没有谈论这个生命周期。)有一大堆,你可以用它们来让你的组件在它们存在的特定点上“做些什么”,或者处理特定的情况。

练习冒险

我们希望我们已经激发了更多的Lightning组件学习的胃口。这里有几个想法,我们可以做的事情,你可以做的费用应用程序,这将使有趣的探索,这自然适合你刚刚学到的东西。

提交后清除表格

现在,当你点击创建费用按钮,表单保持填充状态。将字段设置为空字符串并不困难,但是什么时候应该这样做?想想你想要的行为,可用性,以及服务器响应方面的各种可能性。 (即除了成功之外)

一旦你决定行为,你把代码放在哪里?听起来很简单,首先,你意识到服务器的响应处理是在开销中,而表单字段在expenseForm中。这听起来像一个工作?

显示“费用保存”消息

当您的费用成功保存到服务器上时,最好向用户显示一点成功信息。也许这消息是不同的更新报销?复选框与创建新的费用。或者,也许有时你根本不显示消息。

处理失败

如果字段上的数据验证规则阻止您保存服务器上的记录,会发生什么情况?或者发生其他一些错误?现在什么都没有该应用程序默默地失败。为了报销?复选框,该应用程序可以显示一个不正确的状态的费用。这两个都不好!

处理简单的故障实际上并不是那么多的代码行。但是在开始之前,你需要做一些阅读。

允许费用记录编辑

这是一种先进的,但你可以分阶段解决它。首先,当您单击费用列表中的费用时,请填写相应的费用值。然后让它将创建费用按钮的文本更改为保存费用。 (不要欺骗,并说它节省费用!)然后改变表单触发更新现有费用,而不是创建一个新的事件。

而就此,我们今天就离开你了。再次恭喜,我们希望您能在您的Lightning组件应用开发冒险中找到名气,财富和兴奋!

Lightning-组件(8)事件

学习目标

完成本单元后,您将能够:

  • 为您的应用定义自定义事件。
  • 从组件控制器创建和激发事件。
  • 创建动作处理程序来捕获和处理其他组件发送的事件。
  • 将一个大的组件重构成更小的组件。

连接组件与事件

在本单元中,我们将解决我们的小开支应用程序中最后一个未完成的功能:报销?复选框。你可能认为实现一个复选框将是一个简短的话题。我们当然可以采取一些捷径,并将其作为一个很短的话题。

但是除了使复选框工作之外,这个单元还有关于删除我们已经采取的所有快捷方式。我们要花这个单位“正确地做”,其中有几个地方意味着我们之前做过的重构工作。

在开始之前,我们先来谈谈我们采取的捷径,正确的道路,为什么正确的道路(有点)更难,但也更好。

组成和分解

如果您以源代码的形式看看我们的小费用应用程序,并列出单独的代码工件,您将得到如下内容。

  • expenses component
    • expenses.cmp
    • expensesController.js
    • expensesHelper.js
  • expensesList component
    • expensesList.cmp
  • expenseItem component
    • expenseItem.cmp
  • ExpensesController (server-side)
    • ExpensesController.apex

以下是所有内容如何组合在一起的情况,以及稍后您接线的createExpense和updateExpense事件。

The expenses app is made up of many smaller components.但是,如果你看屏幕上的应用程序,你看到了什么?你应该看到什么,你看到的最终会看到什么,是应用程序分解成更多的组件。您会看到,您可以将我们的应用程序进一步分解为更小的碎片,比迄今为止所做的更多。至少,我们希望您看到“添加费用”表单实际上应该是它自己的独立组件。 (这就是为什么我们在用户界面上画一个盒子的原因!)

为什么我们不把这个表格作为一个单独的组件?不这样做是迄今为止我们在这个模块的过程中最大的捷径。在软件设计方面,这比我们称之为“恶心”的黑客更糟。构建Lightning组件应用程序的正确方法是创建独立的组件,然后将它们组合在一起以构建新的更高级别的功能。为什么我们不采取这种方法?

我们采取了快捷方式,因为它将主要费用数组组件属性和影响它的控制器代码保存在同一个组件中,所以我们在“主费用”组件中保留了“添加费用”表单。我们希望createExpense()辅助函数能够直接触碰费用数组。如果我们将“添加费用”表单移到单独的组件中,那是不可能的。

为什么不?我们很早就介绍了这个原因,但是现在我们想要真正的琢磨一下。闪电组件应该是独立的。它们是封装所有基本功能的独立元素。一个组件不允许触及另一个组件,甚至是一个子组件,并且改变它的内部组件。

有两种主要的方式与另一个组件进行交互或影响其他组件。第一种方法就是我们已经看到并做了相当多的工作:在组件的标签上设置属性。组件的公共属性构成了其API的一部分。

与组件交互的第二种方式是通过事件。与属性一样,组件声明它们发出的事件以及它们可以处理的事件。和属性一样,这些公共事件构成了组件的公共API的一部分。我们实际上已经使用和处理了事件,但事件隐藏在一些便利功能之后。在这个单元中,我们将把事件拖入光明中,并创建一些我们自己的事物。

布线电路隐喻再次

这两种机制(属性和事件)是API“套接字”,即将组件连接在一起形成完整电路的方式。事件也在幕后,流经电路的电子。但这只是事件与属性不同的一种方式。

当您将<lightning:button>上的onclick属性设置为组件控制器中的操作处理程序时,将在这两个组件之间创建一个直接关系。他们是联系在一起的,当他们使用公共API保持彼此独立时,他们仍然是耦合的。

事件是不同的。组件不会将事件发送到其他组件。事实并非如此。组件广播特定类型的事件。如果有一个组件对这种类型的事件作出响应,并且该组件“听到”了你的事件,那么它就会采取行动。

您可以将属性和事件之间的区别视为有线电路和无线电路之间的区别。我们在这里不是说无线电话。一个组件不能获得另一个组件的“编号”并调用它。那将是一个属性。不,事件就像无线广播。你的组件到达收音机,并发出一条消息。有没有人打开收音机,并调整到正确的频率?你的组件没有办法知道 – 所以你应该以这样的方式编写你的组件,如果没有人听到他们广播的事件。 (也就是说,事情可能不起作用,但什么都不应该崩溃。)

从组件发送事件

好的,足够的理论,让我们做一些特定的应用程序,看看事件如何在代码中工作。我们将开始实施报销?复选框。然后,我们将采取我们所学到的,并用它来重构“添加费用”表格到自己的组件,这是伟大的工程师的意图。

首先,我们将重点放在Reimbursed__c字段的<lightning:input>上的点击处理程序。

<lightning:input type="toggle" 
            label="Reimbursed?"
            name="reimbursed"
            class="slds-p-around--small"
            checked="{!v.expense.Reimbursed__c}"
            messageToggleActive="Yes"
            messageToggleInactive="No"
            onchange="{!c.clickReimbursed}"/>

在我们深入点击处理程序之前,让我们回过头来看一下<lightning:input>必须提供的内容。 type =“toggle”实际上是一个带有切换开关设计的复选框。类使您可以应用自定义CSS样式或使用SLDS实用程序。 messageToggleActive和messageToggleInactive为选中和未选中的位置提供自定义标签。这些便捷的属性只是<lightning:input>上的其他几个属性。最后,<lightning:input>的onchange属性为我们提供了一个简单的方法,可以将切换开关连接到一个动作处理程序,当您向右滑动(选中)或向左滑动(未选中)时更新记录。

现在,让我们考虑一下选中或不选中时会发生什么。根据我们编写的代码来创建新的费用,更新费用可能是这样的。

  1. 获取已更改的费用项目。
  2. 创建一个服务器操作来更新基础费用记录。
  3. 将费用计入行动。
  4. 设置一个回调来处理响应。
  5. 触发操作,将请求发送到服务器。
  6. 当响应到来并且回调运行时,更新费用属性。

呃,什么费用属性?再看看我们的组件标记。没有开支,只是一个单一的开支。嗯,对,这个组件只是一个单一的项目。在费用清单组件上有一个费用属性…但这甚至不是“真正的”费用。真正的是顶级费用组件中的一个组件属性。嗯。

有没有一个component.get(“v.parent”)?或者,它必须是component.get(“v.parent”)。get(“v.parent”) – 让我们得到父母的父母的引用,所以我们可以在那里设置费用?

停止。对。那里。

组件不会触及其他组件,并在其上设置值。没有办法说“嘿,祖父母,我会更新费用。”组件保持自己的手。当一个组件想要一个祖先组件改变某些东西时,它会问。奈斯利。通过发送一个事件。

这是最酷的部分。发送事件看起来几乎与直接处理更新相同。这是clickReimbursed处理程序的代码。

({
    clickReimbursed: function(component, event, helper) {
        var expense = component.get("v.expense");
        var updateEvent = component.getEvent("updateExpense");
        updateEvent.setParams({ "expense": expense });
        updateEvent.fire();
    }
})
哇。这很简单!它看起来有点像我们上面所设想的。 clickReimbursed的上述代码执行以下操作:
  1. 获取更改的费用。
  2. 创建一个名为updateExpense的事件。
  3. 将费用打包到事件中。
  4. Fires 事件。

回调的东西丢失,但否则这是熟悉的。但是…什么是处理调用服务器和服务器响应,并更新主费用数组属性?我们怎么知道这个updateExpense事件呢?

updateExpense是一个自定义事件,也就是我们自己写的一个事件。你可以这样说,因为与获取服务器动作不同,我们使用component.getEvent()而不是component.get()。另外,我们所得到的没有价值提供者,只是一个名字。我们将在一瞬间定义这个事件。

至于什么是处理调用服务器和处理响应,让我们来谈谈它。我们可以在costItem组件中实现服务器请求并处理响应。然后,我们会发送一个事件,只是放弃依赖于费用数组的事情。这将是一个完全有效的设计选择,并将保持费用项目组件完全独立,这是可取的。

但是,正如我们将看到的,创建新费用的代码和更新现有费用的代码非常相似,足以避免重复的代码。因此,我们所做的设计选择是发送一个updateExpense事件,主费用组件将处理。后来,当我们重构我们的表单时,我们也会创建一个新的开销。

通过让所有子组件委托处理服务器请求和管理费用数组属性的责任,我们打破封装一点。但是,如果将这些子组件视为费用组件的内部实现细节,那也没关系。主要费用部分是独立的。

您可以选择:合并关键逻辑或封装。您可以在Lightning Components中进行权衡,就像在任何软件设计中进行权衡一样。只要确保你记录的细节。

定义一个事件

我们要做的第一件事是定义我们的自定义事件。在开发者控制台中,选择 File | New | Lightning Event, 并将事件命名为“updateEvent”。用下面的标记替换默认的内容。

<aura:event type="COMPONENT">
    <aura:attribute name="expense" type="Expense__c"/>
</aura:event>

有两种类型的事件,组件和应用程序。这里我们使用了一个组件事件,因为我们想要一个祖先组件来捕捉和处理这个事件。祖先是组件层次结构中的一个组件“之上”。如果我们想要一个“通用广播”类事件,任何组件都可以接收它,我们将使用应用程序事件。

应用程序和组件事件的完全区别和正确的用法并不是我们能够在这里得到的。这是一个更高级的话题,有足够复杂的细节,这是我们在这个模块中的目的。当你准备好了更多,资源将帮助你。

另外要注意的是事件的定义是多么紧凑。我们在创建时命名了这个事件,其中包括了费用更新日期,它的标记是一个开始和结束的<aura:event>标记和一个<aura:attribute>标记。事件的属性描述了它可以携带的有效载荷。在clickReimbursed操作处理程序中,我们通过调用setParams()来设置有效载荷。在事件定义中,我们看到事件参数是如何定义的,并且没有其他有效的参数。

而这几乎都是定义事件。您不要将实现或行为细节添加到事件本身。他们只是包。事实上,有些事件根本没有任何参数。他们只是消息。 “发生了这种情况!”发生和接收事件的组件中定义了“如果发生这种情况”的所有行为。

发送事件

我们已经看过如何在clickReimbursed操作处理程序中实际触发一个事件。但是要做到这一点,我们需要做最后一件事,那就是注册事件。将这一行标记添加到费用项组件,正好在其属性定义下方。

    <aura:registerEvent name="updateExpense" type="c:expensesItemUpdate"/>

这个标记说我们的组件触发了一个名为“updateExpense”的类型为“c:expensesItemUpdate”的事件。但是,当我们定义它时,不是“expensesItemUpdate”这个事件的名字吗?而组件或应用程序事件类型发生了什么?

你认为这有点令人困惑 – 这实际上是一个开关式的转换。这可能有助于将“应用程序”和“组件”视为Lightning Components框架事件类型,而来自您定义的事件名称的类型是自定义事件类型或事件结构类型。也就是说,当你定义一个事件时,你可以定义一个包的格式。当你注册发送一个事件时,你声明了它使用的格式。

定义和注册一个事件的过程可能看起来有些奇怪,所以让我们先看一下。这里在expenseItem中,我们将发送一个名为updateExpense的事件。稍后在expenseForm中,我们将发送一个名为createExpense的事件。这两个事件都需要包含一笔费用才能保存到服务器上。因此,他们都使用c:expensesItemUpdate事件类型或包格式发送事件。

在接收方,我们的主要支出部分将要注册来处理这两个事件。尽管服务器调用结束了,但用户界面更新略有不同。那么费用如何知道是否在c:expensesItemUpdate包中创建或更新费用?通过正在发送的事件的名称。

了解这里的区别,以及如何将一个事件用于多种目的,是学习Lightning组件的灯泡时刻。如果你还没有那个时刻,那么当你看其余的代码的时候,你就会拥有它。

在开始处理事件之前,让我们总结一下发送它们的过程。

  1. 通过创建闪电事件定义自定义事件,给它一个名称和属性。
  2. 注册您的组件发送这些事件,通过选择一个自定义事件类型,并给这种类型的具体使用一个名称。
  3. 通过以下方式在控制器(或助手)代码中触发事件:
    1. 使用component.getEvent()来创建特定的事件实例。
    2. 用fire()发送事件。

如果你继续执行所有我们刚刚看过的代码,你可以测试一下。重新加载您的应用程序,并切换报销?复选框几次。如果你错过了一步,你会得到一个错误,你应该重新检查你的工作。如果你做的一切正确…嘿,等等,花费改变颜色显示其报销?状态,就像预期的一样!

这个行为在我们开始这个单位之前就已经存在了。这就是<lightning:input>组件具有value =“{!v.expense.Reimbursed__c}”集合的效果。切换开关时,费用的本地版本将更新。但是这个改变没有被发送到服务器。如果您查看Salesforce中的费用记录,或重新加载应用程序,则不会看到更改。

为什么不?我们只做了一半的工作来为我们的活动创建一个完整的电路。我们必须通过在另一端创建事件处理程序来完成电路布线。该事件处理程序将负责将更改发送到服务器,并使更新持久。

处理事件

使费用项组件发送一个事件需要三个步骤。启用费用组件来接收和处理这些事件需要三个并行的步骤。

  1. 定义一个自定义事件。我们已经做到了这一点,因为费用项目正在发送费用正在接收的相同的自定义事件。
  2. 注册组件来处理事件。这将事件映射到动作处理程序。
  3. 实际上在动作处理程序中处理事件。

由于我们已经完成了第一步,我们立即转到第二步,注册费用来接收和处理updateExpense事件。就像注册发送一个事件一样,注册来处理一个事件是一行标记,你应该在init处理程序之后添加到费用组件中。

     <aura:handler name="updateExpense" event="c:expensesItemUpdate"
        action="{!c.handleUpdateExpense}"/>

与init处理程序一样,它使用<aura:handler>标记,并具有为该事件设置控制器操作处理程序的action属性。就像当你在costItem中注册事件一样,在这里你设置了事件的名字和类型 – 尽管注意到这个类型使用了更明智的事件属性。

换句话说,在这里你没有看到太多。什么是新的,特定于处理自定义事件,是属性的组合,并知道如何在费用中的接收器“套接字”与费用项中的发件人“套接字”相匹配。

这完成了这项工作的接线部分。剩下的就是实际编写动作处理程序!

我们将从handleUpdateExpense动作处理程序开始。这里是代码,并确保把它放在clickCreate动作处理器下。

    handleUpdateExpense: function(component, event, helper) {
        var updatedExp = event.getParam("expense");
        helper.updateExpense(component, updatedExp);
    }

呵呵。那很有意思。除了表单验证检查和特定的帮助函数,我们正在委托工作,它看起来像这个动作处理程序是一样的handleCreateExpense。

现在让我们添加updateExpense帮助函数。正如我们对动作处理器所做的那样,请确保将该代码放在createExpense帮助器函数的正下方。

    updateExpense: 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") {
                // do nothing!
            }
        });
        $A.enqueueAction(action);
    },

两件事你应该注意到的蝙蝠。首先,除了回调细节外,updateExpense帮助器方法与createExpense帮助器方法相同。这听起来像机会。

其次,关于这些回调细节。是什么赋予了?怎样才能做正确的事情呢?

想一想。之前,在测试发送事件(如果不是之前)时,我们看到了费用项组件的颜色是否改变以响应已报销的?复选框。记得解释吗?费用记录的本地副本已经更新!所以,至少现在,当服务器告诉我们它已经成功地更新它的版本时,我们不必做任何事情。

请注意,此代码仅处理服务器成功更新费用记录的情况。如果出现错误,我们肯定会有一些工作要做。说,会计将这笔费用标记为无法报销,因此无法将此字段设置为true。但是,正如他们所说,这是另一天的教训。

重构帮助函数

让我们回到我们看到的那个机会来分解一些常见的代码。除了回调之外,这两个辅助函数是相同的。所以,我们来创建一个新的,更广义的函数,将回调作为参数。

    saveExpense: function(component, expense, callback) {
        var action = component.get("c.saveExpense");
        action.setParams({
            "expense": expense
        });
        if (callback) {
            action.setCallback(this, callback);
        }
        $A.enqueueAction(action);
    },
回调参数是可选的。如果它在那里,我们会把它传递给行动。简单。现在我们可以将我们的事件特定的帮助函数减少到下面的代码。
    createExpense: function(component, expense) {
        this.saveExpense(component, expense, function(response){
            var state = response.getState();
            if (state === "SUCCESS") {
                var expenses = component.get("v.expenses");
                expenses.push(response.getReturnValue());
                component.set("v.expenses", expenses);
            }
        });
    },

    updateExpense: function(component, expense) {
        this.saveExpense(component, expense);
    },
createExpense只是稍微短一些,但它完全专注于响应返回时的操作(回调)。哇,updateExpense是一个单线!

重构添加费用表单

那个小小的重构练习是如此令人满意,使用事件是如此(我们很抱歉)让人激动,让我们再做一次,但是做得更大。更多的牛铃!

接下来的任务包括从费用组件中提取“添加费用”表单,并将其移至自己的新组件。提取表单标记很简单,只需简单的复制粘贴练习即可。但是还有什么动作呢?在我们开始移动碎片之前,让我们先思考一下什么是移动和停留。

在当前的设计中,表单的动作处理器clickCreate处理输入验证,将请求发送到服务器,并更新本地状态和用户界面元素。表单仍然需要一个动作处理程序,而且应该还可以处理表单验证。但是我们打算让其余的留下来,因为我们将我们的服务器请求逻辑合并到费用组件中。

所以有一点点(但只有一点点),在那里戏弄。那么我们的计划就是先移动表单标记,然后尽可能少地移动,以使其正常工作。我们将重构这两个组件以通过事件进行通信,而不是直接访问费用数组组件。

让我们开始吧!

在主费用组件中,选择两个<! – CREATE NEW EXPENSE – >注释之间的所有内容,包括开始和结束注释本身。把它剪切到你的剪贴板。 (是的,切,我们承诺)

创建一个新的Lightning组件,并将其命名为“expenseForm”。将复制的“添加费用”表单标记粘贴到<aura:component>标记之间的新组件中。

返回费用组件。将新的expenseForm组件添加到标记中。这部分费用应该是这样的。

    <!-- 新的费用表格 -->
    <lightning:layout >
        <lightning:layoutItem padding="around-small" size="6">
            <c:expenseForm/>
        </lightning:layoutItem>
    </lightning:layout>

此时,您可以重新加载您的应用程序以查看更改。应该没有明显的变化。但是,不出所料,创建费用按钮不再起作用。

让我们快速浏览其余的部分动作。

接下来,将费用组件中的newExpense属性移至expenseForm组件标记。这是用于表单域,所以它需要在表单组件。它移动不需要更改,所以只需从一个剪切并粘贴到另一个。

在expenseForm组件中,创建控制器和帮助器资源。

将clickCreate操作处理程序从费用控制器移至expenseForm控制器。按钮位于表单组件中,因此按钮的操作处理程序也需要在那里。相信与否,这也不需要改变。 (你可能在这里开始感受一个主题。)

现在我们需要做一些实际的改变。但是这些会很熟悉,因为我们只是添加事件发送,这是我们之前做的费用项目。您会记得,expenseItem还会发送一个由费用组件处理的费用负载的事件。

在expenseForm助手中,创建createExpense函数。

    createExpense: function(component, newExpense) {
        var createEvent = component.getEvent("createExpense");
        createEvent.setParams({ "expense": newExpense });
        createEvent.fire();
    },

这看起来非常像在expenseItem中的clickReimbursed操作处理程序。

如果组件要发送事件,则需要注册该事件。将以下内容添加到expenseForm组件标记中,位于newExpense属性下方。

    <aura:registerEvent name="createExpense" type="c:expensesItemUpdate"/>

在这一点上,我们已经完成了所有的工作来实现expenseForm组件。您应该可以重新加载应用程序,并且表单现在“有效”,因为没有错误,您输入无效数据时应该会看到相应的表单消息。如果您使用的是Salesforce Lightning Inspector,则甚至可以看到正在触发expenseItemUpdate事件。剩下的就是处理它。

在我们处理事件之前,请注意这个重构是多么容易。大部分的代码没有改变。在前面的步骤中总共有六行新的代码和标记。今天做这个工作是不熟悉的,但是要做几次,而且你意识到你只是在移动一些代码。

好的,让我们来完成这个。 expenseForm触发createExpense事件,但我们还需要费用组件来捕获它。首先我们注册createExpense事件处理程序,并将其连接到handleCreateExpense操作处理程序。再次,这是一个单一的标记线。将此行添加到updateExpense事件处理程序的正上方或下方。

    <aura:handler name="createExpense" event="c:expensesItemUpdate"
        action="{!c.handleCreateExpense}"/>
最后,在最后一步,在费用控制器中创建handleCreateExpense操作处理程序。在handleUpdateExpense操作处理程序的上方或下方添加此代码。
    handleCreateExpense: function(component, event, helper) {
        var newExpense = event.getParam("expense");
        helper.createExpense(component, newExpense);
    },

是的,那很简单。所有这些工作都委托给createExpense帮助函数,并且不会移动或更改。我们的handleCreateExpense动作处理程序就在那里把正确的东西连接在一起。

于是,我们完成了如何使用事件松散地耦合组件。在一个组件中创建和激发事件,在另一个组件中捕获并处理事件。无线电路!

奖金课初级视觉改进

在我们进入夕阳之前,或更确切地说,挑战之前,这里是两个适度的视觉改善。

首先,你有没有注意到报销和相关日期时间标签在绿色背景上几乎是看不见的蓝色?

Blue labels on green fix

如果它也困扰着你,那么这里是如何解决它的。 (我们一直这样做直到我们将表单重构为自己的组件)。在费用项目组件中,通过单击STYLE按钮添加一个样式资源。用下面的代码替换默认的样式规则。

.THIS.slds-theme_success .slds-form-element__label {
    color: #ffffff;
}
.THIS.slds-theme_success .slds-text-title {
    color: #ffffff;
}
重新加载应用程序的甜美,甜美的眼睛应变救济。

最后,我们想通过添加一些容器组件来改进我们的应用程序的布局。最后一点也让您有机会在所有的变化之后看到完整的费用部分。在费用组件中,将费用列表标记替换为以下内容。

<lightning:layout>
    <lightning:layoutItem padding="around-small" size="6">
        <c:expensesList expenses="{!v.expenses}"/>
    </lightning:layoutItem>
    <lightning:layoutItem padding="around-small" size="6">
        Put something cool here
    </lightning:layoutItem>
</lightning:layout>
这些变化的影响是增加一些边距和填充,并使费用列表更窄。布局留下的空间把东西放在右边。在下一个单元中,我们会建议你可以自己做一些练习。

Lightning-组件(7)服务器端控制器

学习目标

完成本单元后,您将能够:

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

服务器端控制器的概念

到目前为止,我们所做的一切都是严格的客户端。我们还没有将开支节省回到Salesforce。创建一些费用,然后打重装,会发生什么?没错,所有的费用都消失了。呜呼,免费的钱!

除了刚才所说的会计,他们对这种事情都是有些冷淡的。而实际上,我们是不是想要报销那些从我们口袋里拿出来的费用呢?哎呀!我们需要将我们的数据保存到Salesforce,不再有任何延误!

开玩笑,现在是时候把服务器端控制器添加到我们的应用程序。我们一直把这个拿回来,而我们的基础知识倒了。现在你已经准备好了,让我们潜入!

进入一些图片,就是。让我们确保我们知道我们的目标,并且在我们上路之前,我们的油箱已经满了。

首先,让我们重温一下我们在这个模块中看到的第一个图表,一个非常高层次的Lightning组件应用程序的体系结构。

A very high level architecture of Lightning Components: client view and controller, server apex controller and database

到目前为止,我们所看到的一切都在这张照片的客户端上。 (注意我们通过在这里合并控制器和帮助器来简化。)虽然我们引用了一个自定义的对象类型,Expense__c,它是在服务器端定义的,但我们并没有直接触及服务器。

请记住我们如何将不同的元素连接起来以创建一个完整的电路?我们在最后一个单位建立的费用形式可能看起来像这样。

Client side of flow

该电路以创建按钮开始,该按钮连接到clickCreate操作处理程序(1)。当动作处理程序运行时,它会从表单字段(2)中获取值,然后向费用数组(3)添加新的开销。当数组通过设置更新时,触发费用列表(4)的自动重新显示,完成电路。很简单,对吧?

那么,当我们在服务器端访问时,图表会变得更加复杂。更多的箭头,更多的颜色,更多的数字! (暂时解释一切吧。)

Complete flow: client and server-side

而且,这个电路没有相同的平滑,同步的控制流程。服务器通话费用很高,可能需要一些时间。情况良好时为毫秒,网络拥塞时为长时间。 Lightning组件不希望应用程序在等待服务器响应时被锁定。

在等待时保持响应的解决方案是异步处理服务器响应。这意味着什么,当你点击Create Expense按钮时,你的客户端控制器触发一个服务器请求,然后继续处理。它不仅不等待服务器,它忘记了它提出的要求!

然后,当响应从服务器返回时,与请求打包在一起的代码(称为回调函数)将运行并处理响应,包括更新客户端数据和用户界面。

如果你是一个有经验的JavaScript程序员,异步执行和回调函数可能是你的面包和黄油。如果你之前没有和他们一起工作,这将是新的,也许是非常不同的。这也很酷。

从Salesforce查询数据

我们将从阅读Salesforce数据开始,在费用应用程序启动时加载现有费用列表。

注意

如果您尚未在Salesforce中创建一些真正的费用记录,现在将是一个好时机。否则,在执行后面的内容之后,您可能会花时间调试为什么没有加载,实际上什么也没加载。你这卑微的作者是为这一切而堕落的。的。时间。

第一步是创建您的Apex控制器。 Apex控制器包含您的Lightning组件可以调用的远程方法。在这种情况下,查询和接收来自Salesforce的费用数据。

我们来看一下代码的简化版本。在开发者控制台中,创建一个名为“ExpensesController”的新Apex类,并粘贴以下代码。

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];
    }
}
我们将在下一节深入讨论Apex控制器,但现在这是一个非常简单的Apex方法。它运行一个SOQL查询并返回结果。只有两个具体的事情,使这个方法可用于您的闪电组件代码。
  • 方法声明之前的@AuraEnabled注解。
    “Aura”是Lightning组件核心的开源框架的名称。您已经看到它在一些核心标签的命​​名空间中使用,如<aura:component>。现在你知道它来自哪里。
  • 静态关键字。所有@AuraEnabled控制器方法都必须是静态方法,可以是公共范围或全局范围。

如果这些要求提醒您使用Visualforce的JavaScript远程功能的远程方法,那不是巧合。要求是一样的,因为架构在关键点非常相似。

另一件值得注意的事情是该方法没有做任何特殊的事情来打包Lightning组件的数据。它只是直接返回SOQL查询结果。 Lightning组件框架处理大多数情况下涉及的所有编组/解组工作。太好了!

从Salesforce加载数据

下一步是将费用组件连接到服务器端的Apex控制器。这很容易,可能会让你头晕目眩。将费用组件的开头<aura:component>标记更改为指向Apex控制器,如下所示。

<aura:component controller="ExpensesController">
新的部分以粗体突出显示,是的,这真的很简单。

但是,指向Apex控制器并不实际加载任何数据,或者调用远程方法。就像组件和(客户端)控制器之间的自动连线一样,这个指向只是让这两个组件“彼此​​了解”。这个“知道”甚至采取了同样的形式,另一个价值提供者,我们稍后会看到。但是自动布线只有这么远。完成电路仍然是我们的工作。

在这种情况下,完成电路意味着以下。

  1. 当费用组件被加载时,
  2. 查询Salesforce的现有费用记录,和
  3. 将这些记录添加到费用组件属性。

我们将依次采取每一个。第一个项目,当费用组件首次被加载时触发行为,要求我们编写一个init处理程序。这只是一个连接到组件的init事件的动作处理程序的简称,当组件首次创建时会发生这种情况。

你需要的接线是一行标记。将以下内容添加到组件的属性定义之下的费用组件。

<aura:handler name="init" action="{!c.doInit}" value="{!this}"/>
<aura:handler>标签是你如何说组件可以处理特定的事件。在这种情况下,我们说我们将处理init事件,我们将使用控制器中的doInit操作处理程序来处理它。 (设置值=“{!this}”将其标记为“值事件”,这意味着这里太复杂了,只要知道应该将这个属性值对添加到init事件中。

调用服务器端控制器方法

一步下去,两步走。剩下的步骤都在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);
    },

在这里,所有新事物都让你感到迷惘,请注意,这只是另一个动作处理程序。它的格式相同,功能签名相同。我们在熟悉的领域。

也就是说,函数签名后的每一行代码都是新的。我们稍后会看看所有这些,但是这里是这个代码的大纲:

  1. 创建一个远程方法调用。
  2. 设置远程方法调用返回时应该发生的事情。
  3. 排队远程方法调用。

这听起来很简单,对吧?也许代码的结构或特性是新的,但是需要发生什么的基本要求再次被熟悉。

这听起来像我们真的很鼓舞人心?比如说,也许我们试图通过一个粗糙的补丁来指导你?那么,我们需要谈论一些艰难的事情。该问题出现在函数的第一行代码中。

        var action = component.get("c.getExpenses");

这行代码创建我们的远程方法调用,或远程操作。首先,component.get()部分看起来很熟悉。我们已经做了很多次了。

除了…之前,我们得到的是“v.something”之前,v是视图的价值提供者。这里是“c”,是的,c是另一个价值提供者。我们已经在press =“{!c.clickCreate}”和action =“{!c.doInit}”这样的表达式中看到了一个c值提供者。

在视图中,这些表达式在组件标记中。在控制器中,c值提供者表示不同的东西。它代表远程Apex控制器。

“等一下。你是否告诉我,我们有客户端控制器,c默认命名空间,c服务器端控制器,都在Lightning组件中?“

那么,一句话,是的。深呼吸。

看,我们会诚实的对你。如果我们把这一切都做了,我们可能会做出一些不同的选择。虽然我们所做的选择不是事故,但三个“c”绝对是一个混乱的机会。我们也感到困惑!

但是正如他们所说的那样,就是这样。有备则无患。现在你知道了。

识别

上下文

含义

c.

组件标记

客户端控制器

c.

控制器代码

服务器端控制器

c:

标记

默认命名空间

好的,回到我们的代码。在我们偏离之前,我们正在看这条线。

        var action = component.get("c.getExpenses");

在之前的代码中,component.get(“v.something”)返回给我们对视图(组件标记)中的子组件的引用,component.get(“c.whatever”)返回对可用操作的引用在控制器中。在这种情况下,它会向我们的Apex控制器返回一个远程方法调用。这是如何创建对@AuraEnabled方法的调用。

下一行“action.setCallback(…)”是远程方法调用返回时将运行的代码块。由于这种情况发生在“稍后”,所以我们暂且搁置一边。

实际运行的下一行是这一行。

        $A.enqueueAction(action);

我们之前简单地看过$ A,但没有讨论过。这是一个框架全局变量,提供了一些重要的功能和服务。 $ A.enqueueAction(action)将我们刚刚配置的服务器调用添加到Lightning Components框架请求队列中。它连同其他未决的服务器请求将在下一个请求周期中发送到服务器。

这听起来有些模糊。完整的细节非常有趣,对于Lightning组件的高级使用非常重要。但现在,这是你需要知道的$ A.enqueueAction(action)。

  • 它将服务器请求排队。
  • 就你的管制员行动而言,这是结束了。
  • 你不能保证什么时候,或者如果,你会听到回来。

这是我们搁置的代码块的地方。但在我们谈论这个之前,有一点流行文化。

服务器调用,异步执行和回调函数

Carly Rae Jepsen的单曲“Call Me Maybe”于2011年发布,获得批判和商业成功,在十多个国家中名列第一。到目前为止,它已经在全球销售了超过1800万份,显然是有史以来最畅销的数字单曲之一。从合唱中最难忘的一行是“这是我的号码。所以也许打电话给我。“除了乐观和危险的吸引力之外,这是Lightning组件处理服务器调用的一个比喻。

听我们出来。让我们看看我们在伪代码中的动作处理器。

    doInit: function(component, event, helper) {
        // 从Salesforce中加载费用
        var action = component.get("c.getExpenses");
        action.setCallback(
            // 这是我的号码,也许打电话给我
        );
        $A.enqueueAction(action);
    },
嗯。 也许我们应该更详细地解释action.setCallback()的参数。 在真正的动作处理程序代码中,我们称之为如此。
        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());
        }
    },
我们会再说一遍。异步执行和回调函数对JavaScript程序员来说是必须的,但是如果你来自另一个背景,那么可能不太熟悉。希望我们已经把它放在了这个位置,因为它是使用Lightning组件开发应用程序的基础。

处理服务器响应

现在我们已经得到了创建一个服务器请求的结构,让我们来看看我们的回调函数实际处理响应的细节。这里只是回调函数。

    function(response) {
        var state = response.getState();
        if (state === "SUCCESS") {
            component.set("v.expenses", response.getReturnValue());
        }
    }
回调函数采用单个参数,响应,这是一个不透明的对象,提供返回的数据(如果有的话)以及有关请求状态的各种细节。

在这个特定的回调函数中,我们执行以下操作。

  1. 获取响应的状态。
  2. 如果状态是成功的,也就是说,我们的要求按计划完成,那么:
  3. 将组件的费用属性设置为响应数据的值。

你可能有几个问题,比如:

  • 如果响应状态不是成功会发生什么?
  • 如果回应永远不会到来会发生什么? (打电话给我,可能的话。)
  • 我们如何才能将响应数据分配给我们的组件属性?

前两个答案不幸的是,我们不打算在这个模块中介绍这些可能性。他们当然是你需要知道的事情,并考虑在你的真实世界的应用程序,但我们只是没有空间。

最后一个问题在这里是最相关的,但也是最容易回答的。我们为费用属性定义了一个数据类型。

<aura:attribute name="expenses" type="Expense__c[]"/>
而我们的服务器端控制器动作有一个方法签名,它定义了它的返回数据类型。
public static List<Expense__c> getExpenses() { ... }

类型匹配,所以我们可以只分配一个到另一个。闪电组件处理所有的细节。您当然可以自己处理结果,并在应用程序中将其转换为其他数据。但是,如果你正确地设计你的服务器端操作,你不一定非要。

好吧,这是很多不同的方式来看十几行代码。这里的问题是:你有没有尝试过你的我们的应用程序的版本呢?因为我们已经完成了Salesforce部分的加载费用。重新加载应用程序,并查看您在Salesforce中输入的费用是否显示!

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方法saveExpense()。它需要一个Expense(Expense__c)对象并插入它。这使我们可以使用它来创建新的记录和更新现有的记录。

接下来,请注意,我们使用with sharing关键字创建了该类。这将自动将组织的共享规则应用于通过这些方法可用的记录。例如,用户通常只会看到自己的费用记录。 Salesforce会自动在幕后为您处理所有复杂的SOQL规则。

使用with共享关键字是编写服务器端控制器代码时需要采取的基本安全措施之一。但是,这是必要的措施,但还不够。你看到有关执行isAccessible()和isUpdatable()检查的意见吗?分享只会带你到目前为止。尤其是,您需要自己实现对象和字段级别的安全性(您经常会看到缩写为FLS)。

例如,下面是我们的getExpenses()方法的一个版本,该安全性最低限度地实现。

    @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];
    }

这是我们最初的单线的一个扩展,而且还是足够的。另外,描述通话费用很高。如果您的应用程序经常调用此方法,则应该找到一种方法来优化或缓存每个用户的访问权限检查。

和SLDS一样,我们根本没有空间来教授安全Apex编码的所有细节。与SLDS不同,承担您编写的代码的安全性不是可选的。如果您还没有阅读参考资料中的安全编码实践文章,请将其添加到您的队列中。

好, </stern-lecture>.

将数据保存到Salesforce

在我们实施“添加费用”表单之前,不要作弊,我们先来看看创建新记录与阅读现有记录是不同的挑战。使用doInit(),我们只需读取一些数据,然后更新应用程序的用户界面。直截了当,即使我们必须让Carly Rae参与解释。

创建新记录涉及更多。我们将从表单中读取值,在本地创建新的费用记录,发送该记录以保存在服务器上,然后当服务器告诉我们已保存时,使用返回的记录更新用户界面服务器。

这是否会使它听起来像是很复杂?比如,也许我们需要滚石乐队和一整首歌曲来帮助我们解释下一个问题。

让我们来看看一些代码,你可以自己决定。

首先,确保已经保存了Apex控制器的更新版本,包括saveExpense()方法的先前版本。

请记住,当我们向您展示如何处理表单提交?当至少有一个字段是无效的,你会看到一个错误消息,表单不提交。当所有字段都有效时,错误消息将被清除。

因为我们把所有的细节(和所有的作弊)都放到了辅助函数createExpense()函数中,所以我们不需要在控制器中做任何其他的修改。到目前为止,这么容易?

所以,我们所需要做的就是在助手中更改createExpense()函数,以完成前面提到的所有复杂的事情。这是代码。

    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);
    },

这是否像你所期望的那样复杂?多少行?我们希望不是!

事实上,这个动作处理器中只有一件新事物,而且很容易理解。我们来看看代码。

我们首先创建action,使用component.get(“c.saveExpense”)获取新的Apex控制器方法。很熟悉。

接下来,我们将数据有效载荷附加到该操作。这是新的。我们需要将新费用的数据发送到服务器。但看看它是多么容易!您只需使用action.setParams()并提供带有参数名称 – 参数值对的JSON样式的对象。一个技巧,重要的是您的参数名称必须与Apex方法声明中使用的参数名称匹配。

接下来我们设置请求的回调。同样,这是服务器返回响应时会发生的情况。如果您将此回调函数与我们的原始createExpense帮助函数进行比较,则实际上是相同的(减去恶心的黑客)。

就像在之前的版本中一样,我们得到()费用属性,将一个值push()到它上面,然后set()它。唯一真正的区别是,我们不是将我们本地版本的新开销push到数组中,而是推送服务器的响应!

为什么这个工作?因为服务器端方法插入(在这种情况下是新的)记录,其上印上一个ID,然后返回结果记录。服务器端和客户端数据类型再次匹配,所以我们不需要做任何额外的工作。

而且,就是这样。不需要滚石!

需要注意的事项

虽然我们已经介绍了将客户端Lightning组件代码与服务器端Apex代码连接起来的所有必要事项,但是有几件事值得你在知道的地方咬你之前指出。

第一个问题是区分大小写,这一般归结为Apex和Salesforce通常不区分大小写,但JavaScript区分大小写。也就是说,“Name”和“name”在Apex中是相同的,但在JavaScript中是不同的。

这可能会导致绝对疯狂的错误,即使是在你的面前是完全不可见的。特别是如果您一直在Salesforce上使用非Lightning组件代码一段时间,您可能根本不会再考虑对象和字段名称,方法等等的情况。

因此,对于您来说,这是一个最佳实践:始终使用每个对象,字段,类型,类,方法,实体,元素,大象或您有什么的确切API名称。总是在任何地方,即使没有关系。这样,你就不会有问题。或者,至少不是这个问题。

我们希望引起你注意的另一个问题是“必需的”的性质。我们不能拒绝重复一句着名的引语:“你继续使用这个词。我不认为这意味着你的想法。“

在我们迄今为止编写的代码中,我们已经看到至少两种不同的“必需”。在“添加费用”表单的标记中,您会看到使用两种方式的单词。例如,在费用名称字段上。

<lightning:input aura:id="expenseform" 
                 label="Expense Name"
                 name="expensename"
                 value="{!v.newExpense.Name}"
                 required="true"/> 

<lightning:input>标记的必需属性设置为true。这些都说明了所需要的一个含义,即“设置该元素的用户界面以指示该字段是必需的”。换句话说,这只是表面化的。这里没有保护您的数据的质量。

在我们为同一领域编写的验证逻辑中说明了“必需”一词的另一个含义。

var validExpense = component.find('expenseform').reduce(function (validSoFar, inputCmp) {
    // 显示无效字段的错误消息
    inputCmp.showHelpMessageIfInvalid();
    return validSoFar && inputCmp.get('v.validity').valid;
}, true);

“必需的”这个词是无处可见的,但这就是验证逻辑强制执行的。您必须为费用名称字段设置一个值。

而且,就这一点而言,这太棒了。您的费用表格不会以空名称提交新费用。除非,你知道,有一个错误。或者,也许其他一些小部件使用相同的服务器端控制器,但并不仔细地进行表单验证。等等。所以,这对您的数据质量有一些保护,但并不完美。

您如何执行,我们的意思是执行一个数据完整性规则,在这个例子中是关于费用名称?你做服务器端。而不仅仅是服务器端的任何地方。您将规则放在字段定义中,或者将其编码到触发器中。或者,如果你是一个腰带式和吊带式的工程师,就像所有正确的思维工程师一样。

为了真正的数据完整性,当需要“需要”意味着需要的时候,尽可能在最低级别执行。