核心要点
前文探讨了如何使用 @Input
和 @Output
注解将数据传入和传出组件。本文将介绍 Angular 2 组件的另一个基本方面——它们使用 提供者 的能力。
您可能在组件配置属性列表中看到过“提供者”,并且您可能意识到它们允许您定义一组可用于组件的可注入对象。这很好,但当然会引出一个问题,“什么是提供者?”
回答这个问题需要深入探讨 Angular 2 的依赖注入 (DI) 系统。我们可能会在以后的博文中专门介绍 DI,但 Pascal Precht 的一系列文章对此进行了很好的介绍,从这里开始:https://www.php.cn/link/f7f3bfce09a3008d185e1775549ec2d2 DI 和 Angular 2 的 DI 系统(如 Pascal 的文章中所述),但简而言之,DI 系统负责:
在之前的文章中,我们包含了一个图表,显示组件形成以根组件开头的层次结构。让我们补充该图表以包含注入器及其注册的资源(提供者):
图 1:每个组件都有自己的注入器,用于注册提供者。注入器创建子注入器,对提供者的请求从本地注入器开始,并向上搜索注入器层次结构。
从上面我们可以看出,虽然组件形成了一个向下定向图,但它们相关的注入器具有双向关系:父注入器创建子注入器(向下),当请求提供者时,如果组件自己的注入器中找不到请求的提供者,Angular 2 将向上搜索父注入器(向上)。这意味着较低级别具有相同标识符的提供者将遮盖(隐藏)较高级别具有相同名称的提供者。
那么,注入器在每个级别注册的这些“提供者”是什么呢?实际上很简单:提供者是 Angular 用于提供(产生、生成)我们想要使用的资源或 JavaScript“事物”:
不幸的是,术语“提供者”有时既指类、函数或值,也指提供者产生的事物——类实例、函数的返回值或返回值。
让我们看看如何通过使用 MyClass
(一个简单的类,将生成我们想要在应用程序中使用的实例)创建类提供者来向组件添加提供者。
图 2:具有四个属性的简单类。(代码屏幕截图来自 Visual Studio Code)
好了,这就是类。现在让我们指示 Angular 使用它注册类提供者,以便我们可以要求依赖注入系统提供一个实例供我们在代码中使用。我们将创建一个组件 ProvDemo_01.ts
,它将用作应用程序的根组件。我们在 bootstrap.ts
中加载此组件并启动我们的应用程序:
图 3:启动应用程序的 bootstrap.ts
文件,它实例化根组件。
如果上面的内容没有意义,请查看我们之前的文章,该文章介绍了构建简单的 Angular 2 应用程序的过程。我们的根组件称为 ProvDemo
,存储库包含几个不同版本的该组件。您可以通过更新上面导入 ProvDemo
的行来更改显示的版本。我们的根组件的第一个版本如下所示:
图 4:导入 MyClass
的 CompDemo
,将其添加到 providers
数组中,并在构造函数参数中将其用作类型。
向此组件添加 MyClass
提供者很简单:
MyClass
@Component
的 providers
属性MyClass
”的参数在幕后,当 Angular 实例化组件时,DI 系统会为组件创建一个注入器,该注入器注册 MyClass
提供者。然后,Angular 会看到在构造函数的参数列表中指定的 MyClass
类型,并查找新注册的 MyClass
提供者,并使用它来生成一个实例,然后将其分配给“myClass
”(初始小写“m”)。
查找 MyClass
提供者和生成要分配给“myClass
”的实例的过程都是 Angular 完成的。它利用 TypeScript 语法来了解要搜索的类型,但 Angular 的注入器负责查找和返回 MyClass
实例。
鉴于上述情况,您可能会得出结论,Angular 会获取“providers
”数组中的类列表,并创建一个简单的注册表来检索该类。但是,为了提高灵活性,存在一个细微的调整。需要“调整”的一个主要原因是帮助我们编写组件的单元测试,这些组件具有我们不想在测试环境中使用的提供者。对于 MyClass
,没有太多理由不使用真实的东西,但是如果 MyClass
调用服务器来检索数据,我们可能不想或无法在测试环境中这样做。为了解决这个问题,我们需要能够在 ProvDemo
中替换不会进行服务器调用的模拟 MyClass
。
我们如何进行替换?我们是否需要遍历所有代码并将每个 MyClass
引用更改为 MyClassMock
?这效率不高,并且是编写测试的糟糕模式。
我们需要在不更改 ProvDemo
组件代码的情况下替换提供者实现。为了实现这一点,当 Angular 注册提供者时,它会设置一个映射,以将键(称为“令牌”)与实际提供者相关联。在上面的示例中,令牌和提供者是同一事物:MyClass
。将 MyClass
添加到 @Component
装饰器中的 providers
属性是以下内容的简写:
<code>providers: [ provide(MyClass, {useClass: MyClass} ]</code>
这意味着“使用 MyClass
作为令牌(键)来查找提供者,并将提供者设置为 MyClass
,以便当我们请求提供者时,依赖注入系统会返回 MyClass
实例”。我们大多数人都习惯于将键视为数字或字符串。但在这种情况下,令牌(键)是类本身。我们也可以使用字符串作为令牌来注册提供者,如下所示:
<code>providers: [ provide("aStringNameForMyClass", {useClass: MyClass} ]</code>
那么,这如何帮助我们进行测试呢?这意味着在测试环境中,我们可以覆盖提供者注册,有效地执行以下操作:
<code>provide(MyClass, {useClass: MyClassMock}) </code>
这会将令牌(键)MyClass
与类提供者 MyClassMock
关联起来。当我们的代码要求 DI 系统在测试中注入 MyClass
时,我们会得到 MyClassMock
的实例,它可以伪造数据调用。最终效果是所有代码保持不变,我们不必担心单元测试是否会调用在测试环境中可能不存在的服务器。
在上面,我们通过编写以下代码将类提供者实例注入到构造函数中:
<code>constructor( myClass: MyClass ) {...} </code>
TypeScript 允许我们指定 myClass
参数需要是 MyClass
类型,而 DI 系统会完成工作,为我们提供 MyClass
实例。
但是,如果我们使用字符串令牌而不是类,我们如何告诉 Angular 注入我们的提供者结果呢?让我们编辑 bootstrap.ts
文件以添加新的值提供者并使用字符串令牌注册它。请记住,值提供者是一种返回与令牌关联的值的提供者类型。在上面的示例中,我们告诉 Angular 通过添加到 @Component
的 providers
属性来注册提供者,但我们也可以通过将它们传递到引导函数中来注册提供者(可以将相同的内容添加到 providers
属性):
图 5:添加了值提供者的 bootstrap.ts
。
在这里,我们通过调用 provide
函数并传入字符串令牌(“SECURITY_KEY
”)和一个对象来添加提供者,该对象指定我们想要创建一个值提供者以及提供者本身——在本例中是一个简单值。现在,我们想将值提供者生成的值注入到我们的构造函数中,但这行不通……
<code>providers: [ provide(MyClass, {useClass: MyClass} ]</code>
这是因为“SECURITY_KEY
”不是类型。为了使能够注入具有非类令牌的提供者成为可能,Angular 为我们提供了 @Inject
参数装饰器。与所有其他装饰器一样,我们需要导入它,然后我们使用它来告诉 Angular 注入与我们的字符串令牌关联的提供者。为此,我们调整 create ProvDemo_02.ts
:
图 6:导入“Inject
”装饰器并使用它来注入使用字符串令牌标识的值提供者。
我们可以使用相同的语法来注入 MyClass
提供者:
<code>providers: [ provide("aStringNameForMyClass", {useClass: MyClass} ]</code>
好了,我们已经了解了如何注册和使用提供者,但让我们进一步了解提供者返回的内容。
正如我们在上面看到的,提供者负责生成要注入的事物。类提供者会生成一个实例,然后注入该实例。但是,重要的是要理解,每次注入类提供者结果时,您都不会获得一个新实例。相反,DI 系统会生成一次实例,将其缓存,并且只要您使用相同的提供者,后续每次注入都会收到相同的实例。
最后一点很重要,因为每个组件都有自己的注入器及其自己的注册提供者。MyClass
具有设置为当前时间(以毫秒为单位)的时间属性和一个随机数,以帮助我们查看我们每次是否获得相同的实例。我们将向应用程序添加一个 ChildComp
组件。
图 7:将 MyClass
注入到构造函数中的 ChildComp
。
请注意,我们导入 MyClass
并使用它来设置构造函数参数列表中的类型。重要提示:导入的 MyClass
在 ChildComp
中的唯一用途是作为 DI 系统用来查找注册提供者的令牌。 因为 ChildComp
没有使用该令牌注册自己的提供者,所以 Angular 会向上查找注入器层次结构以找到一个。为了使这能够工作,我们需要将 ChildComp
添加到 ProvDemo
组件:
图 8:向模板中添加了 ChildComp
的 ProvDemo
。
我们导入 ChildComp
,向 @Component
添加 directives
属性以告诉 ProvDemo
我们将使用 ChildComp
组件,并将 ChildComp
元素添加到模板。当应用程序运行时,控制台输出显示 ProvDemo
和 ChildComp
都收到相同的 MyClass
实例:
<code>providers: [ provide(MyClass, {useClass: MyClass} ]</code>
现在让我们更改 ChildComp
以向其注入器添加 MyClass
提供者:
图 9:定义了自己的 MyClass
提供者的 ParentComp
。
我们唯一更改的是向 @Component
注解添加 providers
属性。当然,我们可以看到创建了两个不同的 MyClass
实例:
<code>providers: [ provide("aStringNameForMyClass", {useClass: MyClass} ]</code>
Angular 的此功能为任何一个提供者生成的结果以及我们是否要使用单个实例或多个实例提供了很大的灵活性。例如,您可以将组件放在重复器中,以便多次生成组件。如果此重复组件注册自己的提供者,则每个组件都会获得唯一的提供者。但是,如果您只在父组件中注册提供者,则每个重复实例都会共享父组件的提供者。
在本文中,我们定义了什么是提供者,并介绍了三种不同类型的提供者。然后,我们研究了如何为组件注册提供者以及如何将提供者生成的结果注入到组件中。我们还研究了 Angular 如何使用注入器层次结构来查找请求的提供者。Angular 为您提供了更多关于依赖注入系统如何工作以及在何处查找提供者的控制,但以上内容应该可以帮助您开始在 Angular 2 应用程序中创建和使用提供者。
在 Angular 2 中,类是创建对象的蓝图。它封装了数据和操作该数据的函数。另一方面,工厂是一种用于创建对象的模式。在 Angular 2 中,工厂用于创建和配置没有明确类来表示的服务或值。工厂提供了一种根据上下文或配置生成不同类实例的方法。
在 Angular 2 中,组件和提供者协同工作以创建动态且交互式的用户界面。组件是 Angular 应用程序的构建块,而提供者用于创建组件可以使用的服务。提供者允许组件共享数据和功能,从而更易于维护和更新应用程序。
Angular 2 中的值用于向应用程序的其他部分提供配置信息。它们可以注入到控制器、服务和工厂中,允许在运行时配置应用程序的这些部分。这使应用程序更灵活,也更易于测试。
Angular 2 中的类绑定是一种动态地向元素添加和删除 CSS 类的方法。您可以将类绑定到表达式,当该表达式计算结果为 true 时,该类将添加到元素。如果表达式的计算结果为 false,则该类将被删除。这允许您创建动态且交互式的用户界面。
在 Angular 2 的上下文中,API(应用程序编程接口)是一组用于构建和交互软件应用程序的规则和协议。Angular 2 提供了一个丰富的 API,允许开发人员使用更少的代码和精力创建复杂的应用程序。Angular 2 API 包括用于创建组件、服务、指令、管道等的特性。
要在 Angular 2 中使用工厂,您首先需要定义它。这是通过创建一个返回您希望工厂生成的对象的函数来完成的。然后,您可以使用 .factory
方法将此工厂与 Angular 模块注册。注册工厂后,您可以将其注入到应用程序的其他部分,例如控制器和服务。
在 Angular 2 中创建组件涉及定义一个类并使用 @Component
装饰器对其进行装饰。@Component
装饰器告诉 Angular 该类是一个组件,并提供元数据,这些元数据确定如何在运行时处理、实例化和使用该组件。
Angular 2 中的提供者用于创建和向应用程序部分提供服务。要使用提供者,您首先需要将其与 Angular 模块注册。注册后,可以将提供者注入到组件、其他服务甚至其他提供者中。
在 Angular 2 中创建服务涉及定义一个类,该类封装了服务提供的数和函数。然后,此类将使用 @Injectable
装饰器进行装饰,该装饰器告诉 Angular 该类是一个服务,可以注入到应用程序的其他部分。
Angular 2 中的值用于向应用程序的其他部分提供配置信息。要使用值,您首先需要将其与 Angular 模块注册。注册后,可以将值注入到控制器、服务和工厂中。
以上是Angular 2组件和提供商:类,工厂和价值观的详细内容。更多信息请关注PHP中文网其他相关文章!