AngularDart(咱们一般在这个文档中简单地称为Angular)是一个框架,用于在HTML和Dart中构建客户端应用程序。它是做为Angular包发布的,与 其余许多Dart包同样,能够经过Pub工具得到。html
您能够经过使用Angular的标记组合HTML 模板,编写组件类来管理这些模板,在服务中添加应用程序逻辑以及在模块中装入组件和服务来编写Angular应用程序。java
而后,经过引导根模块启动应用程序。 Angular接管,根据您提供的说明在浏览器中呈现您的应用内容,并响应用户交互。
固然,除此以外还有更多。 您将在后面的页面中了解详细信息。 如今就着眼于大局。git
架构图标识了Angular应用程序的八个主要构建块:程序员
了解这些积木,你就在路上。github
Angular应用程序是模块化的; 也就是说,应用程序由许多模块组装而成。api
在本指南中,术语module是指Dart编译单元,例如库或包。 若是Dart文件除去library或part命令,那么该文件自己就是一个库,所以也是一个编译单元。有关编译单元的更多信息,请参阅Dart语言规范中的“库和脚本”一章。浏览器
每一个Angular应用程序至少有一个模块,即根模块。 虽然根模块多是小应用程序中的惟一模块,但大多数应用程序都有更多的功能模块,每一个模块都是专用于应用程序域,工做流程或紧密相关的一组功能的一致代码块。服务器
最简单的根模块定义了一个单独的根组件类,例如:lib / app_component.dart(class)架构
class AppComponent {}
按照惯例,根组件的名称是AppComponent。
Angular全体就像是Angular包内的库的集合。 主要的Angular库是angular,大多数app模块导入以下:
import 'package:angular/angular.dart';
Angular包有其余重要的库,如angular.security。
一个组件控制屏幕中的一小块视图。
例如,如下视图由组件控制:
您能够在一个类中定义一个组件的应用程序逻辑 - 它支持视图的功能。 该类经过属性和方法的API与视图交互。
例如,这个HeroListComponent有一个heroes属性,返回从服务中获取的英雄列表。 HeroListComponent还有一个selectHero()方法,当用户点击从列表中选择一个英雄时,它会设置一个selectedHero属性。lib/src/hero_list_component.dart (class)
class HeroListComponent implements OnInit { List<Hero> heroes; Hero selectedHero; final HeroService _heroService; HeroListComponent(this._heroService); void ngOnInit() { heroes = _heroService.getHeroes(); } void selectHero(Hero hero) { selectedHero = hero; } }
Angular建立,更新和销毁组件如同用户在应用程序中行走。 您的应用程序能够经过可选的生命周期挂钩在今生命周期中的每一个时刻采起行动,如上面声明的ngOnInit()。
您可使用其配套模板定义组件的视图。 模板是一种HTML形式,告诉Angular如何呈现组件。
模板看起来像普通的HTML,除了一些不一样之处。 这是咱们的HeroListComponent的一个模板:
lib/src/hero_list_component.html
<h2>Hero List</h2> <p><i>Pick a hero from the list</i></p> <ul> <li *ngFor="let hero of heroes" (click)="selectHero(hero)"> {{hero.name}} </li> </ul> <hero-detail *ngIf="selectedHero != null" [hero]="selectedHero"></hero-detail>
虽然这个模板使用了典型的HTML元素,如<h2>和<p>,但它也有一些不一样之处。 相似于* ngFor,{{hero.name}},(click),[hero]和<hero-detail>的代码使用Angular的模板语法。
在模板的最后一行,<hero-detail>标签是一个自定义元素,表明一个新的组件HeroDetailComponent。
HeroDetailComponent是与您正在审阅的HeroListComponent不一样的组件。 HeroDetailComponent(代码未显示)显示关于特定英雄的详情,这是用户从HeroListComponent提供的列表中选择的英雄。 HeroDetailComponent是HeroListComponent的一个子项。
注意<hero-detail>是如何在原生HTML元素中合适的存放。 自定义组件与原生HTML在相同的布局中无缝混合。
元数据告诉Angular如何处理一个类。
回顾HeroListComponent的代码,你能够看到它只是一个类。 没有一个框架的痕迹,没有Angular的特定代码。
实际上,HeroListComponent实际上只是一个类。 直到你告诉Angular它是一个组件。要告诉Angular HeroListComponent是一个组件,请将元数据附加到该类。在Dart中,您可使用注解附加元数据。
如下是HeroListComponent的一些元数据,@Component注解标识紧接着它下面的类做为一个组件类:
lib/src/hero_list_component.dart (metadata)
@Component( selector: 'hero-list', templateUrl: 'hero_list_component.html', directives: const [CORE_DIRECTIVES, formDirectives, HeroDetailComponent], providers: const [HeroService], ) class HeroListComponent implements OnInit { // ··· }
注解一般具备配置参数。 @Component注解须要参数提供Angular须要的信息来建立和呈现组件及其视图。
如下是一些可能的@Component参数:
@Component中的元数据告诉Angular从哪里获取为组件指定的主要构建块。
模板,元数据和组件一块儿描述一个视图。
以相似的方式应用其余元数据注解以指导Angular行为。 @Injectable,@Input和@Output是一些比较流行的注解。
建筑外包是你必须添加元数据到你的代码,以便Angular知道该怎么作。
若是没有框架,您将负责将数据值推送到HTML控件中,并将用户响应转化为操做和值更新。 用手写这样的推/拉逻辑是单调乏味,容易出错的,并且像任何经验丰富的jQuery程序员都能证实的那样是一场恶梦。
Angular支持数据绑定,这是一种协调模板部分与组件部分的机制。 添加绑定标记到模板HTML告诉Angular如何链接双方。
如图所示,有四种形式的数据绑定语法。 每一个表单都有一个方向 - 从DOM到DOM,或者在两个方向。
HeroListComponent示例模板有三种形式:
lib / src / hero_list_component.html(binding)
<li>{{hero.name}}</li> <hero-detail [hero]="selectedHero"></hero-detail> <li (click)="selectHero(hero)"></li>
双向数据绑定是一个重要的第四种形式,它使用ngModel指令将属性和事件绑定在一个符号中。 如下是HeroDetailComponent模板的一个例子:lib/src/hero_detail_component.html (ngModel)
<input [(ngModel)]="hero.name">
在双向绑定中,与属性绑定同样,数据属性值将从组件输入到输入框中。 用户的更改也会返回到组件,将属性重置为最新值,就像事件绑定同样。
Angular在每一个JavaScript事件循环中处理全部数据绑定,从应用程序组件树的根到全部子组件。
数据绑定在模板及其组件之间的通讯中起着重要的做用。
数据绑定对于父组件和子组件之间的通讯也很重要。
Angular模板是动态的。 当Angular呈现它们时,它根据指令给出的指示转换DOM。
指令是一个带有@Directive注解的类。 一个组件是一个指令与模板; 一个@Component注解其实是一个@Directive注解,扩展了面向模板的特性。
虽然组件在技术上是指令,但组件对于Angular应用程序来讲是很是独特和重要的,因此这种架构概述将组件与指令分开。
还有其余两种指令:结构和属性指令。
它们倾向于以属性的形式出如今元素标签内,有时候以名称的形式出现,但更常见的是做为赋值或绑定的目标。
结构指令经过添加,删除和替换DOM中的元素来改变布局。
示例模板使用两个内置的结构指令:
lib / src / hero_list_component.html(structural)
<li *ngFor="let hero of heroes"></li> <hero-detail *ngIf="selectedHero != null"></hero-detail>
在Dart中,惟一值为true的是布尔值true; 全部其余值是错误的。 JavaScript和TypeScript相反,将诸如1和大多数非空对象的值视为true。 出于这个缘由,这个应用程序的JavaScript和TypeScript版本可使用selectedHero做为* ngIf表达式的值。 Dart版本必须使用布尔运算符!=替换。
属性指令会改变现有元素的外观或行为。 在模板中,它们看起来像常规的HTML属性,所以也就是名称。
实现双向数据绑定的ngModel指令是一个属性指令的例子。 ngModel经过设置其显示值属性并响应更改事件来修改现有元素(一般是<input>)的行为。lib/src/hero_detail_component.html (ngModel)
<input [(ngModel)]="hero.name">
Angular还有一些指令能够改变布局结构(例如,ngSwitch)或修改DOM元素和组件的方面(例如ngStyle和ngClass)。
固然,你也能够编写你本身的指令。 像HeroListComponent这样的组件是一种自定义指令。
服务是一个普遍的类别,包含您的应用程序所需的任何值,功能或特征。
几乎任何东西均可以成为服务。 服务一般是一个狭义的,明确的目的。 它应该作一些具体的事情,并作好。
例子包括:
Angular中没有特别指定服务。 Angular没有定义服务。 没有服务基础类,没有地方注册服务。
然而,服务是任何Angular的应用程序的基础。 组件占据了服务的半壁江山。
如下是一个输出到浏览器控制台的日志服务类的示例:lib/src/logger_service.dart (class)
class Logger { void log(Object msg) => window.console.log(msg); void error(Object msg) => window.console.error(msg); void warn(Object msg) => window.console.warn(msg); }
这是一个HeroService,使用Future来获取英雄。 HeroService取决于日志服务和另外一个处理服务器频繁通讯工做的BackendService。
lib / src / hero_service.dart(class)
class HeroService { final BackendService _backendService; final Logger _logger; final heroes = <Hero>[]; HeroService(this._logger, this._backendService); List<Hero> getHeroes() { _backendService.getAll(Hero).then((heroes) { _logger.log('Fetched ${heroes.length} heroes.'); this.heroes.addAll(heroes as List<Hero>); // fill cache }); return heroes; } }
服务无处不在。
组件类应该是精益的。 他们不从服务器获取数据,验证用户输入或直接登陆到控制台。 他们将这些任务委托给服务。
一个组件的工做是启用用户体验,仅此而已。 它在视图(由模板呈现)和应用程序逻辑(一般包括模型的一些概念)之间起中介做用。 一个好的组件提供了数据绑定的属性和方法。 它委托一切不重要的服务。
Angular不强制执行这些原则。 若是您用3000行代码编写“kitchen sink”组件,它不会抱怨。
Angular经过简单地将应用程序逻辑分解为服务,并经过依赖注入将这些服务提供给组件,从而帮助您遵循这些原则。
依赖注入是一种提供一个类的新实例的方法,它须要完整的依赖关系。 大多数依赖是服务。 Angular使用依赖注入来为新组件提供他们须要的服务。
Angular能够经过查看构造函数参数的类型来判断组件须要哪些服务。 例如,你的HeroListComponent的构造函数须要一个HeroService:lib/src/hero_list_component.dart (constructor)
final HeroService _heroService; HeroListComponent(this._heroService);
当Angular建立一个组件时,它首先要求一个注入器来提供组件须要的服务。
注入器维护一个先前建立的服务实例的容器。 若是请求的服务实例不在容器中,那么在将服务返回给Angular以前,注入器将建立一个并将其添加到容器中。 当全部请求的服务已经解析并返回时,Angular能够用这些服务做为参数调用组件的构造函数。 这是依赖注入。
HeroService注入的过程看起来有点像这样:
若是注射器没有HeroService,它如何知道如何制做一个?
简而言之,您必须事先在注入器中注册HeroService的提供者。 提供者是能够建立或返回服务的东西,一般是服务类自己。
不管应用程序组件树中的级别如何,您均可以在引导期间或组件中注册提供程序。
注册提供者最多见的方法是使用@Component注解providers参数在组件级别:lib/app_component.dart
@Component( // ··· providers: const [BackendService, HeroService, Logger], ) class AppComponent {}
使用组件注册意味着您将得到该组件的每一个新实例的服务新实例。 经过组件提供的服务与应用程序组件树中的全部组件的后代共享。
引导时注册提供程序的状况很是少见。 有关详细信息,请参阅依赖注入页面的配置注入部分。
关于依赖注入的要点:
包起来
您已经了解了关于Angular应用程序的八个主要构建块的基础知识:
这是一个Angular应用程序中全部其余应用程序的基础,并且这足够了。但它并不包括你须要知道的一切。
如下是其余重要的Angular功能和服务的简短字母顺序列表。