AngularDart4.0 指南-体系结构概述 顶

AngularDart(我们通常在这个文档中简单地称为Angular)是一个框架,用于在HTML和Dart中构建客户端应用程序。它是作为Angular包发布的,与 其他许多Dart包一样,可以通过Pub工具获得。

您可以通过使用Angular的标记组合HTML 模板,编写组件类来管理这些模板,在服务中添加应用程序逻辑以及在模块中装入组件和服务来编写Angular应用程序。

然后,通过引导根模块启动应用程序。 Angular接管,根据您提供的说明在浏览器中呈现您的应用内容,并响应用户交互。 当然,除此之外还有更多。 您将在后面的页面中了解详细信息。 现在就着眼于大局。

架构图标识了Angular应用程序的八个主要构建块:

  • 模块
  • 组件
  • 模板
  • 元数据
  • 数据绑定
  • 指令
  • 服务
  • 依赖注入

了解这些积木,你就在路上。

本页面引用的代码作为一个实例(查看源代码)提供。 

模块

Angular应用程序是模块化的; 也就是说,应用程序由许多模块组装而成。

在本指南中,术语module是指Dart编译单元,例如库或包。 如果Dart文件除去library或part命令,那么该文件本身就是一个库,因此也是一个编译单元。有关编译单元的更多信息,请参阅Dart语言规范中的“库和脚本”一章。

每个Angular应用程序至少有一个模块,即根模块。 虽然根模块可能是小应用程序中的唯一模块,但大多数应用程序都有更多的功能模块,每个模块都是专用于应用程序域,工作流程或紧密相关的一组功能的一致代码块。

最简单的根模块定义了一个单独的根组件类,例如:lib / app_component.dart(class)

class AppComponent {}

按照惯例,根组件的名称是AppComponent。

Angular 库

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参数:

  • selector:CSS选择器,告诉Angular创建并插入这个组件的一个实例,它在父HTML中找到一个<hero-list>标签。 例如,如果应用程序的HTML包含<hero-list> </ hero-list>,则Angular将在这些标记之间插入一个HeroListComponent视图的实例。
  • templateUrl:这个组件的HTML模板的模块相对地址,如上所示。
  • directives:该模板需要的组件或指令列表。 为了Angular处理出现在模板中的应用标签,比如<hero-detail>,标签对应的组件必须在指令列表中声明。
  • providers:组件需要的服务的依赖注入提供者列表。 这是告诉Angular这个组件的构造函数需要HeroService的一种方法,这样它就可以获得显示的英雄列表。

@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>
  • {{hero.name}}插值在<li>元素中显示组件的hero.name属性值。
  • [hero]属性绑定将来自父HeroListComponent的selectedHero的值传递给子HeroDetailComponent的hero属性。
  • 当用户点击英雄的名字时,(click)事件绑定调用组件的selectHero方法。

双向数据绑定是一个重要的第四种形式,它使用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>
  • * ngFor告诉Angular在英雄列表中标出每个英雄的一个<li>。
  • * ngIf仅在选择的英雄存在时才包含HeroDetail组件。

在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元素和组件的方面(例如ngStylengClass)。 当然,你也可以编写你自己的指令。 像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应用程序中所有其他应用程序的基础,而且这足够了。但它并不包括你需要知道的一切。

以下是其他重要的Angular功能和服务的简短字母顺序列表。

  • Forms:支持基于HTML验证和脏检查的复杂数据录入方案。
  • HTTP:与服务器通信以获取数据,保存数据并使用HTTP客户端调用服务器端操作。
  • Lifecycle hooks:通过实现生命周期钩子接口,触及组件生命周期中的关键时刻,从创建到销毁。
  • Pipes:通过转换显示值来改善用户体验。
  • Router:在客户端应用程序中从一个页面到另一个页面进行导航,而不会离开浏览器
  • Testing:为您的应用编写组件测试和端到端测试。

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏葡萄城控件技术团队

一个Web页面的问题分析

几个月之前我接到一个新的开发任务,要在一个旧的Web页面上面增添一些新的功能。在开发的过程中发现旧的代码中有很多常见的不合适的写法,结合这些问题,如何写出更好的...

2079
来自专栏快乐八哥

Windows8异步编程的注意事项

Windows8 App开发中涉及到下载数据和上传数据。针对小的数据使用的是WinJS.xhr(),而针对大的文件,不方便使用post方式直接上传的文件都使用B...

21610
来自专栏禅林阆苑

Vue2.5源码阅读笔记01—代码结构与初始化

Vue作为当下最流行的渐进式的js框架,其渐进式的思想、虚拟DOM的运用、组件化的开发模式、响应式数据侦听原理值得开发者进行探索学习,其中运用的代码组织的技巧,...

73159
来自专栏Youngxj

EMLOG自适应emMsg提示信息

2228
来自专栏十月梦想

node项目中文乱码解决

上一篇的第一个node项目成功运行,但是设置res.write('你好,世界'),浏览器运行出现乱码

913
来自专栏编程之旅

PHP开发——yii2多图上传组件的使用

最近在使用yii2开发一个表单页面的时候,有多图上传的需求,稍微找了找这方面的组件,基本都安利fileInput这个组件,于是就尝试着使用这个库来完成后端表单页...

2901
来自专栏前端小作坊

网页上的复制与剪切

IE 10及以上的版本修改了Document.execCommand()方法,增加了对剪切和复制的支持。Chrome从43版本开始也支持了这项特性。

1282
来自专栏葡萄城控件技术团队

WebStorm 常用功能的使用技巧分享

WebStorm 是 JetBrain 公司开发的一款 JavaScript IDE,使用非常方便,可以使编写代码过程更加流畅。 本文在这里分享一些常用功能的使...

2858
来自专栏用户2442861的专栏

VS2013的常用快捷键以及使用技巧

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/haluoluo211/article/details...

2152
来自专栏葡萄城控件技术团队

Angular的12个经典问题,看看你能答对几个?(文末附带Angular测试)

Angular作为目前最为流行的前端框架,受到了前端开发者的普遍欢迎。不论是初学Angular的新手,还是有一定Angular开发经验的开发者,了解本文中的12...

2198

扫码关注云+社区

领取腾讯云代金券