2017 年 3 月

第 32 卷,第 3 期

此文章由机器翻译。

孜孜不倦的程序员 - 如何成为 MEAN: Angular 组件

通过Ted Neward | 2017 年 3 月

Ted Neward欢迎回来,MEAN。

上个月我开始我下降到 Angular-开始使用它,使用基本模板,以及采取快速看一些更明显部分组成的简单角度"Hello World"应用程序 (msdn.com/magazine/mt793274)。很明显 (以前称为 Angular 2) 的 Angular 构建单页面应用程序 (Spa) 比许多其他 JavaScript Web 框架,包括与其上一级,AngularJS (以前称为 Angular 1) 采用稍有不同的方法。但这些差异并不只是开放源代码 peevishness 在想要执行此操作以不同的方式只是为了以不同的方式; 执行的结果一种构建 Web 应用程序的新方法旨在构建感兴趣,groundswell 和 Angular 已选择用两只手上获取。

Web 的新单词是"组件。

组件

有两种方式来考虑一下单词组件。一个是分解构成了传统的 Angular 应用程序的主要部件。该列表是相对简短,类似于 Angular 或其他 Web 框架︰

  • 模块
  • 组件
  • 服务
  • 路由

其他几个部分都隐藏在 Angular 世界里,但是大多数情况下,这些构成 Angular 应用程序的四个关键组件。

模块是顶级的隔离和 cognition 单位。从根本上来说,几个模块构成了应用程序,其中一个模块是围绕一规范的合理耦合组较小的部分 (组件、 服务等) 的容器。模块通常通常是部署单元,这是什么浏览器会下载到用户的计算机才能执行。更重要的是,它们只是单位的认知分离方式.NET 程序集是完全相同。如果所有的代码、 资源和与"查看和显示当前时间"相关的其他相关资产的方式组合到一个单独的"时间"模块,使用系统的开发人员无需考虑时间,除非他们正在与 (或在中),否则该模块。

这减少了开发人员尝试跟踪的系统中的所有移动部件的总体认知负担。很明显,开发人员可以自由地将所有内容都放入单个模块,就像是当然可以用来编写整个桌面应用程序在一个.NET 程序集中。但是,大多数开发人员提供任意数量的下其安全带体验将具有某些观点上应绘制这些模块行的位置,并且它们将相应地模块化其代码。(任何两个开发人员是否对这些行在哪里,另一方面,达成一致是完全不同的情况。)

组件是类似于模块,但较小的 writ — 其中模块可以包含的操作方式与 HTTP 通信 (如 Angular 的 http 模块) 或 HTML 窗体 (FormsModule) 相关的所有定义,组件就是通常的单一用途和直接相关的用户界面。在从上月的专栏 Hello World 应用程序中,代码必须恰好一个组件 — AppComponent — 提供其问候世界上的简单、 非交互式文本显示。尽管它十分简单,它是,是一个 full-blooded 的 Angular 组件,组件可以通过使用更多或较不透明的方式,这一事实依然不少<my-app>担任组件的标记的标记。</my-app>从本质上讲,标记使用率也变得客户端使用组件的"表面区域"。

另一方面,服务都更像通常提供对不应是组件本身的一部分的基础功能的访问权限的低级别库。在 Angular 方法中,应绑定到服务并中需要它的组件只需使用通常使任何种类的 HTTP API 调用 (如节点/Express/Mongo 需要后端,过去一年,有已 Microsoft 黑客出)。通常情况下,将有更少 services,而在给定的 Angular 应用程序中的组件不但当然,"您的实际效果可能有所不同。"

最后,路由是导航的主要机制。路由定义与 Angular 应用程序应如何响应传入 URL 模式的映射。编写本着 Express.js 的将路由到类似、 将"路由映射"可从一个位置,以便参考中定义的路由和路由通常将映射到一个组件 (尽管该组件又可能 — 并且通常会 — 使其他组件的使用)。

这看起来可能有点使不堪重负,尤其是大量使用单词"组件,"但实际上,它是比表面看上去更简单。让我们首先定义一个新的组件,从 AppComponent 应用程序组件 (有时为称为"根组件"因为它是该操作的开始位置) 中引用的显示名称和一个按钮,单击时,打印的名称写入控制台。它是一个简单的组件,但它将用作简单的方法,若要了解如何构建了一个新组件,引用该对象,并使用它。

GreetingsComponent

按照约定,Angular 等服务以后缀的任何组件键入单词"组件后,"以便于此组件设计为提供问候语,因为 GreetingsComponent 似乎是一种对应 — 如果令人乏味的 — 为其名称。它将位于名为 greetings.component.ts 的文件,但该文件应居住在哪里不是自动的决策;一些角速度开发人员更喜欢将每个组件放入其自己的目录,如问候语,或一些会将其放入 components 目录。另一方面,当然,您可以始终只使其右 app.component.ts 旁边。辩论范围 Angular 开发人员关于即高级选项,但为简单起见,我们将执行此操作为 app.component.ts,到对等,以便 greetings.component.ts 直接位于应用程序目录。

Greetings.component.ts 的期初短语是非常类似于 AppComponent 从上一次︰

import { Component } from '@angular/core';
@Component({
  selector: 'greetings',
  template: '<div>Hello</div>'
})
export class GreetingsComponent {
}

它是实际上只是一个简单的标签。没有复杂,无法在这里看到内容。

但是,使用它,意味着您必须以接通 GreetingsComponent 到 AppComponent,以便 AppComponent 的 HTML 中将包含它,如下所示︰

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1><greetings></greetings>`,
})
export class AppComponent  { name = 'Angular'; }

此处的唯一更改是在 AppComponent"模板"的元数据。(我们会发现更好的归宿,为了让此操作之前生存时间太长,别担心; 您不打算在您组件的类文件中有大量的 HTML。)

如果,但是,保存新修改的 app.component.ts,Angular 将重新加载 (假定运行"npm 开始,"或者它仍从上个月运行),并且不会看到"Hello"消息。这是因为 GreetingsComponent 并未尚未加载,因此不能识别 Angular<greetings>标记作为其中一个是针对组件 placeholding。</greetings>为此,您破解打开应用程序模块文件、 app.module.ts,并在注册 GreetingsComponent 作为您自己的一个︰

import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { AppComponent }  from './app.component';
import { GreetingsComponent } from './greetings.component';
@NgModule({
  imports:      [ BrowserModule ],
  declarations: [ AppComponent, GreetingsComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }

唯一的更改是将"导入"拉入 GreetingsComponent 从磁盘并将其注册为"声明"添加 @NgModule 元数据修饰 AppModule 类中。假定一切的拼写正确,且文件存在,将显示文本"Hello"。进度 !

但是,现在您需要对组件本身进行一些更改 — 和此处的关键在于,所做的更改是给组件,整齐地封装,而不适用于任何其他操作。

添加属性

首先,一般的"Hello"似乎稀奇。让我们将字段添加到 GreetingsComponent 以便为其提供至少某种程度的个性化设置︰

import { Component } from '@angular/core';
@Component({
  selector: 'greetings',
  template: '<div>Hello my name is {{name}}.'
})
export class GreetingsComponent {
  constructor() {
    this.name = 'Tarzan'
  }
}

一旦保存该文件,您应看到"Hello 我的名字是 Tarzan"会显示。"Name"属性是只是标准 TypeScript 属性 — 什么特别之处存在。

但是,那就更好如果而不是硬编码到类作为"Tarzan。"无法从外部看,确定名称 这是 @Input 属性的角色︰

import { Component, Input } from '@angular/core';
@Component({
  selector: 'greetings',
  template: '<div>Hello my name is {{name}}.'
})
export class GreetingsComponent {
  @Input() name : string;
  constructor() { }
}

请注意两件事︰ 首先,您强类型化的属性名称,这样,TypeScript 可以帮助确保只有字符串获取传递控制。其次,@Input() 为它的前面添加批注。这将告知 Angular 属性的实际数据应来自父组件,你现在修改 app.component.ts 如下所示的标记使用情况︰

@Component({
  selector: 'my-app',
  template: `<h1>Hello {{name}}</h1><greetings name="Tarzan"></greetings>`,
})
export class AppComponent  { name = 'Angular'; }

请注意,此处; 重载"name"名称是在 AppComponent,内部使用一个属性,但它也是 GreetingsComponent,一个属性并这两个明确独立。当然,如果将名称从 AppComponent 传递给 GreetingsComponent 作为目标,完成此操作通过使用 {{name}} 插值绑定内 GreetingsComponent 的模板,而不是原义"Tarzan。"

在许多情况下,传递到组件的输入会是两者不是一个字符串;在这些情况下,此类的属性绑定的语法将对执行稍有不同的强制转换,如下所示︰

<user-profile [user]="currentUser"></user-profile>

此处,用户属性属于非字符串类型 (可能类似于用户配置文件),而 currentUser 是当前经过身份验证的用户。您稍后会看到此语法的详细信息,当您开始使用非基元类型 (如演讲者类) 来保存由组件使用的数据。

添加方法

Angular 组件包括,其核心,只是 TypeScript 类,因此很容易地将新方法添加到类,如"执行操作,"打印到控制台的名称︰

@Component({
  selector: 'greetings',
  template: '<div>Hello my name is {{name}}.</div>'
})
export class GreetingsComponent {
  @Input() name : string;
  constructor() { }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

SayMyName 方法将执行完全,只打印到浏览器控制台的名称属性。但是,通常这些方法将想要通过用户执行任何操作调用,这意味着,它们必须绑定到浏览器事件,并且需要另一个略有不同的语法将方法绑定到 Angular 事件,如按钮单击。因此如果 GreetingsComponent 作为其 HTML 模板的一部分定义了一个按钮,然后它可以绑定按钮的 click 事件,以调用 sayMyName,如下所示︰

@Component({
  selector: 'greetings',
  template: '<div>Hello my name is {{name}}.' +
    ' <button (click)="sayMyName()">Say my name</button></div>'
})
export class GreetingsComponent {
  @Input() name : string;
  constructor() { }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

事件绑定语法看起来类似于传统的 HTML 事件绑定只不过 Angular 坚持事件绑定必须 (因此它的 (单击) 而不是 onClick),用圆括号括起来;属性绑定使用方括号内,且事件绑定使用圆括号。它是有点怪,若要查看在第一次,但随着时间开始变得更适应,并且我敢说方便,因为现在符号使其更加清晰的是属性,哪些是事件。

删除 HTML

如果生活在 TypeScript 代码内部的 HTML 的想法感觉有点怪,该组件会让生活在外部文件,该模板通常遵循 component.html 去 component.ts 一起的命名约定。因此,例如,如果 GreetingsComponent 想要在一个单独的文件中保留其整个 HTML,它将进入 greetings.component.html,并且在组件本身的 @Component 元数据中使用 templateUrl 属性引用︰

@Component({
  selector: 'greetings',
  templateUrl: './app/greetings.component.html'
})
export class GreetingsComponent {
  @Input() name : string;
  constructor() { }
  sayMyName() {
    console.log('My name is', this.name)
  }
}

请注意在 templateUrl 使用,因为它是相对于作为一个整体应用程序的根目录的因此即使 greetings.component.html 搁置 greetings.component.ts 的右侧时,必须从"app"目录的父节点引用的 HTML 文件的 URL。

总结

Angular 的方法来构建 Web 应用程序开始变得更加清晰的错误信息︰ 开发人员应定义粒度较小的组件,然后将它们组合到窗体更大规模的应用程序的各种方式。它并不是一个新的概念;实际上,这表明返回到 Visual Basic 3 的美好时光和其他随心所欲工具的年代。Angular 团队只需进入 Web 世界中,这些概念向前移动,和结合一些更现代的工具,可让您清晰和更类似于 Web 的体验。当然,开发人员可以将整个应用程序定义为单个组件,但这不会占用长时间之前的痛苦,以及尝试这样做将大大超过性能提升 (如果存在任何 — 我并不确信将有)。

很多仍然需要完成,但某些路径开始以清楚地︰ 如果此应用程序将演讲者的数据库,我们需要定义某些 SpeakerComponents,最低保护措施,以进行显示和编辑演讲者。与后端通信,需要的服务或两个。将一些装饰物和边缘周围的灯可能会几个程序组件,然后让用户可以使用某些 Url 某些路由。挂起紧张,还有很多尚未出现。在此期间,祝您编码愉快 !


Ted Neward 是本部位于西雅图的 Polytechnology 公司的顾问、讲师和导师。他曾写过 100 多篇文章,是 F # MVP,和具有创作,并且与人合著过十几本书。如果您有兴趣请他参与您的团队工作,请通过 ted@tedneward.com 与他联系,或通过 blogs.tedneward.com 访问其博客。

衷心感谢以下技术专家对本文的审阅: Ward Bell