Angular2知识概括

184 阅读21分钟

@TOC

Angular版本更迭

起源和版本(1~2):

  • Augular 1.x: ①近年来,Web 开发技术的发展日新月异,各种框架层出不穷。在这样的大背景之下,2010年10月,Google首次发布了自己的 Web开发框架,名为 AngularJS,也叫Angular,或者简称为ng。 ②在Angular框架中,提出很多新的思想和概念,比如受到众多开发者喜爱和热议的“双向数据绑定”,在同时期以及之后的web框架中也都出现了“数据绑定”概念,包括最近大热的React、Vue等框架。除了上述特点还有MVC指令模块$scopecontroller依赖注入路由等。 ③在 Google 内部有非常多的产品都在使用Angular,在国内,也有非常多的企业正在使用Angular1.x 开发自己的应用,包括移动APP,其中有很多行业巨头。
  • Angular1.x显然非常成功,那么,为什么要剧烈地转向Angular2?性能限制 <1>AngularJS当初是提供给设计人员用来快速构建HTML表单的一个内部工具。随着时间的推移,各种特性 被加入进去以适应不同场景下的应用开发。然而由于最初的架构限制(比如绑定和模板机制),性能的 提升已经非常困难了。 ②快速变化的WEB <1>在语言方面,ECMAScript6的标准已经完成,在2015年6月正式发布了。这意味着浏览器将很快支持,例如:模块、类、lambda表达式、generator等新的特性,而这些特性将显著地改变JavaScript的开发体验。 <2>在开发模式方面,Web组件也将很快实现。然而现有的框架,包括Angular1.x对WEB组件的支持都不够好。 ③移动化 <1>如今Web环境已经发生了显著地变化,到处都是手机和平板。Angular1.x没有针对移动 应用特别优化,并且缺少一些关键的特性,比如:缓存预编译的视图、触控支持等。 ④简单易用 <1>说实话,Angular1.x太复杂了,学习曲线太陡峭了,这让人望而生畏。Angular团队希望在Angular2中将复杂性封装地更好一些,让暴露出来的概念和开发接口更简单。 <2>Angular不只是试图跟上,他们还推动了大量的标准的应用,增强了现有的应用架构。
  • Angular2: ①2014年9月,Angular团队在 ng-europe会议上公开宣布了第二个大版本的开发计划,也就是大家熟知的 Angular2,与此对应,之前的版本就称为Angular 1.x了。 ②目前,Angular2已经于2016年9月正式发布。Angular2依然保持了最初的核心设计理念,但进行了简化。Angular2的核心灵魂只有一个,那就是组件化(Component),而其它那些细碎的东西,比如Service、Route、Pipe,都是utils 而已。 ③因此,在使用 Angular2 的时候,开发者只要学会使用 Coponent 就解决了一大半问题。但是与 Angular 1.x相比,在各种语法细节上发生了大幅度的修改。
  • Angular 1.x 和 Angular2.x对比:删掉 $scope 作用域 <1>在 Angular 1.x 里面,$scope 是一个相当强大又相当可怕的东西,由于在很多需要回调的场景之下,脏值检测机制无法感知到$scope 上发生的变化,因此经常需要开发者自己手动调用$apply() 方法。典型的场景有:事件回调、setTimeout回调、Ajax回调等等。 <2>Angular2 响应社区的强烈呼吁,删除(或者说隐藏)了 $scope的概念,开发者不再需要感知到它的存在。另外,Angular2在底层引入了 zone.js,所以即使在各种回调函数中修改数据模型也不需要手动调用$apply() 方法了。 ②删掉了ng-controller 指令 <1>这就意味着 Controller 不再是一个独立的组件,它合并到了 Component 内部。这是一个非常大的演进,因为从大量的实战经验来看,在复杂的业务逻辑中复用Controller 几乎是不可能的。 <2>在其它同类的前端框架里面也有类似的处理手法,例如 Backbone 虽然也强调 MVC 的概念,但是它也没有定义单独的Controller 类,Controller也是合并在 View里面编写的。 ③改进脏值检测机制 <1>众所周知,“双向数据绑定”之所以能运行,是因为Angular 底层有“脏值检测”这么一个神奇的机制。而实际上Angular 1.x 里面的脏值检测机制的运行效率非常差,这就是为什么大家一直在抱怨绑定的对象不能太多、太深的原因。 <2>Angular2 大幅度演进了这一机制,不仅引入了单向绑定,还增加了各种检测策略,例如:只检测一次、利用JIT 动态生成脏值检测代码等等。毫无疑问,有了这些工具之后,数据绑定效率不再是问题。 ④嵌套路由 <1>Angular 1.x 里面有一个非常讨厌的问题,框架内置的路由机制不支持嵌套使用,这就导致开发者在日常的开发过程中不得不依赖于第三方的ui-router 库。Angular2没有这个问题了,因为 Angular2的路由是基于 Component的,天然支持嵌套。 ⑤依赖注入机制 <1>Angular2 中的依赖注入写法与 Java中的注解(Annotation)非常类似,如果你熟悉Spring 注解的用法,那么使用Angular2 的依赖注入几乎没有学习成本。当然,概念上是有区别的,Angular2中叫 Decorator(装饰器),更加贴近Python 里面的Decorator 的概念。 ⑥基于 TypeScript 开发 <1>这是最大的一个变更,有很多人担忧这样是否会带来比较大的学习成本,实际的情况并非如此。因为 TypeScript的语法与 Java 或者 C# 非常类似,因此对于从后端转过来的开发者来说,学习这门语言几乎是没有难度的。 <2>还有一个重要的方面需要大家注意:TypeScript 是 Microsoft 开发的一门语言,Google+Microsoft这样的组合会产生多么强大的推动力,大家可以想象。 <3>Google 和 Microsoft本身都是重要的浏览器厂商,Chrome和 IE 加起来的市场份额占据了一大半的市场份额,未来如果两款浏览器内建 TypeScript引擎,很显然 TypeScript和 Angular的前景将会一片光明。这一优势是大量的同类技术框架根本无法企及的,因此大家在做技术选型的过程中需要综合考虑这些情况作出理性的决策。 ⑦自带 UI 控件库 <1>两个版本的 UI 控件库都实现了Material Design 所提出来的设计风格,Material Design是 Google提出来的一种 UI设计原则,终极的目标是:用一套 UI设计规范来兼容各种各样的设备,例如桌面、平板、大屏幕的电视、车载系统、甚至 watch,从而保证用户体验的一致性。

Angular 4.x:

  • 为什么有Angular1.x、Angular2.x,没听说有Angular3.x就直接到了Angular4.x? ①根据语义化命名版本规则(参考:semver.org/lang/zh-CN/…
  • Angular 4 在2017年03月24日正式发布,这里还是先明确下官方的说法,别被angular发布4.0正式版吓到了。 <1>从2.0开始angular.js已经不叫angular.js了,就叫angular(少了.js)只是版本号是2.x。而日常我们为了区别angular.js 1.x和angular的2.x的版本,称之为angular2。 <2>后来官网发现这样的命名方式并不好,决定将版本号语义化(具体可以参考语义化版本 2.0.0)。但是之前route组件因为破坏性升级版本号到了3.x,为了统一版本号,直接将angular提至4.0版本,这样才有了angular发布了4.0正式版。
  • angular.js1.x到angular2确实是一个非常大的升级,以至于api、实现原理、框架思路都是完全不一样的。可以毫不夸张的说,学习angular2就和学一个全新的框架并没有太大区别。
  • BUT!angular2.x到angular4.0破坏性升级并不多,也很容易直接升级到4.0版本。
  • 相比于Angular 2,Angular4的功能列表中添加了许多新功能,同时还有一些旧功能的改进。Angular4是基于angular2 的新加了功能。

angular2入门

运行条件:

  • 由于目前各种环境(浏览器或Node)暂不支持ES6的代码,所以需要一些shim和polyfill(IE需要)让ES6写的代码能够转化为ES5形式并可以正常运行在浏览器中。
  • 从图中可以看出在 Es5 浏览器下需要以下模块加载器: ①systemjs - 通用模块加载器,支持AMD、CommonJS、ES6等各种格式的JS模块加载。es6-module-loader - ES6模块加载器,systemjs会自动加载这个模块。traceur - ES6转码器,将ES6代码转换为当前浏览器支持的ES5代码,systemjs会自动加载这个模块。 在这里插入图片描述

Angular 2 架构:

  • Angular2 是一个完整的单页应用开发框架,它提供了很多组件。
  • Angular 2的架构包含以下模块: ①Module ②Component ③Template ④Metadata ⑤Data Binding ⑥Service ⑦Directive ⑧Dependency Injection
  • 模块: ①模块组件的特征在于可以用于执行单个任务的代码块。 您可以从代码(如类)中导出值的值。 Angular应用程序被称为模块,并使用许多模块构建您的应用程序。 Angular 2的基本构建块是可以从模块导出的组件类。 ②某些应用程序的组件类名称为 AppComponent ,您可以在名为 app.component.ts 的文件中找到它。 使用 export 语句从模块中导出组件类, ③export 语句指定它是一个模块,它的 AppComponent 类定义为public,并且可以被应用程序的其他模块访问。
export class AppComponent { }
  • 组件: ①组件是具有模板的控制器类,主要处理页面上的应用程序和逻辑的视图。 它是一个可以在整个应用程序中使用的代码。 组件知道如何呈现自己和配置依赖注入。 您可以使用组件内联样式,样式网址和模板内联样式将CSS样式与组件相关联。 ②要注册组件,我们使用 @Component 注释,可以将应用程序拆分为更小的部分。 每个DOM元素只有一个组件。
  • 模板: ①组件的视图可以通过使用模板来定义,该模板告诉Angular如何显示组件。 例如,下面的简单模板显示如何显示名称: ②要显示该值,可以在插值大括号中放置模板表达式。
<div>
Your name is : {{name}}
</div>
  • 元数据: ①元数据是处理类的一种方式。 考虑我们有一个称为 MyComponent 的组件,它将是一个类,直到我们告诉Angular它是一个组件。 您可以使用元数据来告诉Angular MyComponent 是一个组件。 元数据可以使用装饰器附加到TypeScript。 ②@Component 是一个装饰器,它使用配置对象来创建组件及其视图。 selector 创建组件的实例,其中它找到< mylist> 父HTML中的标记。 模板告诉Angular如何显示组件。 指令装饰器用于表示组件或指令的数组。
@Component({
   selector : 'mylist',
   template : '<h2>Name is Harry</h2>'
   directives : [MyComponentDetails]
})
export class ListComponent{...}
  • 数据绑定: ①数据绑定是通过声明源和目标HTML元素之间的绑定来协调应用程序数据值的过程。 它将模板部分与组件部分和模板HTML组合,使用标记绑定以连接两侧。 有四种类型的数据绑定: <1>插值:它显示div标签中的组件值。 <2>属性绑定:将属性从父级属性传递给子级的属性。 <3>事件绑定:当您单击组件方法名称时触发。 <4>双向绑定:此表单通过使用 ngModel 指令在单个符号中绑定属性和事件。
  • 服务: ①服务是仅负责执行特定任务的JavaScript函数。 角度服务使用依赖注入机制注入。 服务包括应用程序所需的值,功能或功能。 一般来说,服务是一个类,它可以执行某些特定的事情,如日志服务,数据服务,消息服务,应用程序的配置等。在Angular中没有什么关于服务,并且没有ServiceBase类,但仍然服务可以被视为 基本的角度应用。
  • 指示: ①该伪指令是表示元数据的类。 有三种类型的指令: <1>组件指令:它使用视图和控制器创建自定义控制器,并用作自定义HTML元素。 <2>装饰指令:它使用额外的行为装饰元素。 <3>模板指令:它将HTML转换为可重用的模板。
  • 依赖注入: ①依赖注入是一种将对象作为应用程序中不同组件中的依赖关系传递的设计模式。 它创建一个新的类的实例及其所需的依赖项。使用依赖注入时,必须记住以下几点: <1>依赖注入被刺激到框架中,并且可以在任何地方使用。 <2>注入器机制维护服务实例,并且可以使用提供者创建。 <3>提供者是创建服务的一种方式。 <4>您可以与注入器一起注册提供程序。

Angular2架构

模块 (Modules):

  • 模块由一块代码组成,可用于执行一个简单的任务。
  • Angular 应用是由模块化的,它有自己的模块系统:NgModules。每个 Angular 应该至少要有一个模块(根模块),一般可以命名为:AppModule。
  • Angular 模块是一个带有 @NgModule 装饰器的类,它接收一个用来描述模块属性的元数据对象。
  • 几个重要的属性如下: ①declarations (声明) - 视图类属于这个模块。 Angular 有三种类型的视图类: 组件 、 指令 和 管道 。 ②exports - 声明( declaration )的子集,可用于其它模块中的组件模板 。 ③imports - 本模块组件模板中需要由其它导出类的模块。 ④providers - 服务的创建者。本模块把它们加入全局的服务表中,让它们在应用中的任何部分都可被访问到。 ⑤bootstrap - 应用的主视图,称为根组件,它是所有其它应用视图的宿主。只有根模块需要设置 bootstrap 属性中。
  • 一个最简单的根模块:
app/app.module.ts 文件:
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
  imports:      [ BrowserModule ],
  providers:    [ Logger ],
  declarations: [ AppComponent ],
  exports:      [ AppComponent ],
  bootstrap:    [ AppComponent ]
})
export class AppModule { }
  • 接下来我们通过引导根模块来启动应用,开发过程通常在 main.ts 文件中来引导 AppModule ,代码如下:app/main.ts 文件:
import { platformBrowserDynamic } from '@angular/platform-browser-dynamic';
import { AppModule } from './app.module';
 
platformBrowserDynamic().bootstrapModule(AppModule);

组件 (Components):

  • 组件是一个模板的控制类用于处理应用和逻辑页面的视图部分。
  • 组件是构成 Angular 应用的基础和核心,可用于整个应用程序中。
  • 组件知道如何渲染自己及配置依赖注入。
  • 组件通过一些由属性和方法组成的 API 与视图交互。
  • 创建 Angular 组件的方法有三步: ①从 @angular/core 中引入 Component 修饰器 ②建立一个普通的类,并用 @Component 修饰它 ③在 @Component 中,设置 selector 自定义标签,以及 template 模板

模板 (Templates):

  • Angular模板的默认语言就是HTML。
  • 我们可以通过使用模板来定义组件的视图来告诉 Angular 如何显示组件。以下是一个简单是实例:
<div>
网站地址 : {{site}}
</div>
  • 在Angular中,默认使用的是双大括号作为插值语法,大括号中间的值通常是一个组件属性的变量名。

元数据 (Metadata):

  • 元数据告诉 Angular 如何处理一个类。
  • 考虑以下情况我们有一个组件叫作 Component ,它是一个类,直到我们告诉 Angular 这是一个组件为止。
  • 你可以把元数据附加到这个类上来告诉 Angular Component 是一个组件。
  • 在 TypeScript 中,我们用 装饰器 (decorator) 来附加元数据。
实例
@Component({
   selector : 'mylist',
   template : '<h2>菜鸟教程</h2>'
   directives : [ComponentDetails]
})
export class ListComponent{...}
  • @Component 装饰器能接受一个配置对象,并把紧随其后的类标记成了组件类。
  • Angular 会基于这些信息创建和展示组件及其视图。
  • @Component 中的配置项说明: ①selector - 一个 css 选择器,它告诉 Angular 在 父级 HTML 中寻找一个 标签,然后创建该组件,并插入此标签中。 ②templateUrl - 组件 HTML 模板的地址。 ③directives - 一个数组,包含 此模板需要依赖的组件或指令。 ④providers - 一个数组,包含组件所依赖的服务所需要的依赖注入提供者。

数据绑定 (Data Binding):

  • 数据绑定为应用程序提供了一种简单而一致的方法来显示数据以及数据交互,它是管理应用程序里面数值的一种机制。 ①新版本的Angular已经彻底重写了脏检查机制,提供了极其高效的脏检查机制。

  • 通过这种机制,可以从HTML里面取值和赋值,使得数据的读写,数据的持久化操作变得更加简单快捷。

  • 如图所示,数据绑定的语法有四种形式。每种形式都有一个方向——从 DOM 来、到 DOM 去、双向,就像图中的箭头所示意的: 在这里插入图片描述

  • 插值 : 在 HTML 标签中显示组件值。

<h3>
{{title}}
<img src="{{ImageUrl}}">
</h3>
  • 属性绑定: 把元素的属性设置为组件中属性的值。
<img [src]="userImageUrl">
  • 事件绑定: 在组件方法名被点击时触发。
<button (click)="onSave()">保存</button>
  • 双向绑: 使用Angular里的NgModel指令可以更便捷的进行双向绑定。
<input [value]="currentUser.firstName"
       (input)="currentUser.firstName=$event.target.value" >

指令 (Directives):

  • Angular模板是动态的 。当 Angular 渲染它们时,它会根据指令对 DOM 进行修改。

  • 指令是一个带有"指令元数据"的类。在 TypeScript 中,要通过 @Directive 装饰器把元数据附加到类上。

  • 在Angular中包含以下三种类型的指令: ①属性指令:以元素的属性形式来使用的指令。 ②结构指令:用来改变DOM树的结构 ③组件:作为指令的一个重要子类,组件本质上可以看作是一个带有模板的指令。

  • 示例: ①*ngFor 告诉 Angular 为 sites 列表中的每个项生成一个 <li> 标签。 ②*ngIf 表示只有在选择的项存在时,才会包含 SiteDetail 组件。

<li *ngFor="let site of sites"></li>
<site-detail *ngIf="selectedSite"></site-detail>

服务 (Services):

  • Angular2中的服务是封装了某一特定功能,并且可以通过注入的方式供他人使用的独立模块。

  • 服务分为很多种,包括:值、函数,以及应用所需的特性。

  • 例如,多个组件中出现了重复代码时,把重复代码提取到服务中实现代码复用。

  • 以下是几种常见的服务: ①日志服务 ②数据服务 ③消息总线 ④税款计算器 ⑤应用程序配置

  • 以下实例是一个日志服务,用于把日志记录到浏览器的控制台:

export class Logger {
  log(msg: any)   { console.log(msg); }
  error(msg: any) { console.error(msg); }
  warn(msg: any)  { console.warn(msg); }
}

依赖注入 (Dependency Injection):

  • 控制反转(Inversion ofControl,缩写为IoC),是面向对象编程中的一种设计原则,可以用来减低计算机代码之间的耦合度。其中最常见的方式叫做依赖注入(Dependency Injection,简称DI),还有一种方式叫"依赖查找"(Dependency Lookup)。

  • 通过控制反转,对象在被创建的时候,由一个调控系统内所有对象的外界实体,将其所依赖的对象的引用传递给它。也可以说,依赖被注入到对象中。

  • 在传统的开发模式中,调用者负责管理所有对象的依赖,循环依赖一直是梦魇,而在依赖注入模式中,这个管理权交给了注入器(Injector),它在软件运行时负责依赖对象的替换,而不是在编译时。这种控制反转,运行注入的特点即是依赖注入的精华所在。

  • Angular 能通过查看构造函数的参数类型,来得知组件需要哪些服务。 例如, SiteListComponent 组件的构造函数需要一个SiteService:

constructor(private service: SiteService) { }
  • 当 Angular 创建组件时,会首先为组件所需的服务找一个注入器( Injector ) 。
  • 注入器是一个维护服务实例的容器,存放着以前创建的实例。
  • 如果容器中还没有所请求的服务实例,注入器就会创建一个服务实例,并且添加到容器中,然后把这个服务返回给 Angular 。
  • 当所有的服务都被解析完并返回时, Angular 会以这些服务为参数去调用组件的构造函数。 这就是依赖注入 。

Angular 2 模板语法

Angular 2 模板语法简介:

  • 模板扮演的是一个视图的角色,简单讲就是展示给用户看的部分。
  • 有以下: ①HTML ②插值表达式 ③模板表达式 ④模板语句 ⑤绑定语法 ⑥属性绑定 ⑦HTML 属性、 class 和 style 绑定 ⑧事件绑定 ⑨使用 NgModel 进行双向数据绑定 ⑩内置指令 ⑪* 与 <template> ⑫模板引用变量 ⑬输入输出属性 ⑭模板表达式操作符

HTML:

  • HTML 是 Angular 模板的"语言",除了 \<script\> 元素是被禁用的外 ,其他 HTML 元素都是支持的,例如:
<h1>我的第一个 Angular 应用</h1>

插值表达式:

  • 插值表达式的语法格式为:{{ ... }}。
  • 插值表达式可以把计算的字符串插入HTML中,也可以作为属性值来使用。
<h3>
  {{title}}
  <img src="{{imageUrl}}" style="height:30px">
</h3>

模板表达式:

  • {{ ... }} 里头其实就是一个模板表达式,Angular 会对其进行求值并转化为字符串输出。
  • 以下实例是两个数相加:
<!-- "The sum of 1 + 1 is 2" -->
<p>The sum of 1 + 1 is {{1 + 1}}</p>
  • 我们可以使用 getVal() 来获取这个表达式的值:
<div class="example">
<div class="example_code">
[mycode3 type="html"]
<!-- "4" -->
<p>{{1 + 1 + getVal()}}</p>
  • 模板表达式类似 JavaScript 的语言,很多 JavaScript 表达式也是合法的模板表达式,但不是全部。
  • 以下 JavaScript 表达式是禁止的: ①赋值表达式(=,+=,-=...) ②new操作符 ③带有;或者'的连接表达式 ④自增和自减操作(++和--) 其他与Javascript语法不同的值得注意的包括: ⑤不支持位运算符(|和&) ⑥模板表达式的操作符,如|和?.等,被赋予了新的含义

属性绑定:

  • 模板的属性绑定可以把视图元素的属性设置为模板表达式 。
  • 最常用的属性绑定是把元素的属性设置为组件中属性的值。 下面这个例子中, image 元素的 src 属性会被绑定到组件的 imageUrl属性上:
<img [src]="imageUrl">
  • 当组件为 isUnchanged( 未改变 ) 时禁用一个按钮:
<button [disabled]="isUnchanged">按钮是禁用的</button>
```xml
 - 设置指令的属性:
[ngClass]绑定到classes 属性
``` - 设置一个自定义组件的属性(这是父子组件间通讯的重要途径): ```xml ```

HTML 属性(Attribute)、 class 和 style 绑定:

  • 模板语法为那些不太适合使用属性绑定的场景提供了专门的单向数据绑定形式。

  • 属性(Attribute)、绑定:当元素没有属性可绑的时候,使用HTML标签属性(Attribute)绑定。考虑 ARIA, SVG 和 table 中的 colspan/rowspan 等属性(Attribute) 。它们是纯粹的属性 。 它们没有对应的属性可供绑定。

  • 以下实例会报错:

<tr><td colspan="{{1 + 1}}">Three-Four</td></tr>
  • 我们会得到这个错误:
Template parse errors:
Can't bind to 'colspan' since it isn't a known native property

模板解析错误:不能绑定到'colspan',因为它不是已知的原生属性
  • 正如提示中所说, 元素没有 colspan 属性。 但是插值表达式和属性绑定只能设置 属性 ,而不是 Attribute,所以需HTML标签 Attribute 绑定来创建和绑定类似的Attribute。
  • HTML标签特性绑定在语法上类似于属性绑定,但中括号中的部分不是一个元素的属性名,而是由一个attr.的前缀和HTML标签属性的名称组成,然后通过一个能求值为字符串的表达式来设置HTML标签属性的值。如:
<table border=1>
  <tr><td [attr.colspan]="1 + 1">One-Two</td></tr>
  <tr><td>Five</td><td>Six</td></tr>
</table>
  • css类绑定:借助 CSS 类绑定 ,我们可以从元素的 class 属性上添加和移除 CSS 类名。
  • CSS 类绑定在语法上类似于属性绑定。但方括号中的部分不是一个元素的属性名,而是包括一个 class 前缀,紧跟着一个点 (.) ,再跟着CSS 类的名字组成。 其中后两部分是可选的。例如: [class.class-name] 。
  • 下面的例子展示了如何通过css类绑定类添加和移除"special"类:
<!-- 标准HTML样式类设置 -->
<div class="bad curly special">Bad curly special</div>
<!-- 通过绑定重设或覆盖样式类  -->
<div class="bad curly special" [class]="badCurly">Bad curly</div>
<!-- 通过一个属性值来添加或移除special样式类 -->
<div [class.special]="isSpecial">这个样式比较特殊</div>
  • style样式绑定:通过样式绑定,可以设置内联样式。样式绑定语法上类似于属性绑定,但中括号里面的部分不是一个元素的属性名,样式绑定包括一个style.,紧跟着css样式的属性名,例如:[style.style-property]。
<button [style.color] = "isSpecial ? 'red': 'green'">红色</button>
<button [style.background-color]="canSave ? 'cyan': 'grey'" >保存</button>
 
<!-- 带有单位的样式绑定 -->
<button [style.font-size.em]="isSpecial ? 3 : 1" ></button>
<button [style.font-size.%]="!isSpecial ? 150 : 50" ></button>
  • 样式属性可以是中线命名法(font-size),也可以是驼峰是命名法(fontSize)。

事件绑定:

  • 在事件绑定中,Angular通过监听用户动作,比如键盘事件、鼠标事件、触屏事件等来响应相对应的数据流向-从视图目标到数据源。
  • 事件绑定的语法是由等号左侧小括号内的 目标事件 和右侧引号中的 模板声明 组成。
  • 比如下面这个例子,是事件绑定监听按钮的点击事件。只要点击鼠标,都会调用组件的 onSave()方法。
<button (click)="onSave()">保存</button>
  • 圆括号中的名称 ——比如 (click) ——标记出了目标事件。在下面这个例子中,目标是按钮的 click 事件。
<button (click)="onSave()">Save</button>
  • 也可以使用on- 前缀的形式:
<button on-click="onSave()">On Save</button>

使用 NgModel 进行双向数据绑定:

  • 当开发数据输入表单时,期望的结果是既能将组件的数据显示到表单上,也能在用户修改时更新组件的数据。
  • 以下是一个通过 [(NgModel)] 来实现双向绑定:
<input [(ngModel)]="currentUser.firstName">
  • []实现了数据流从组件到模板,()实现了数据流从模板到组件,两者一结合[()]就实现了双向绑定。

  • 使用前缀形式的语法:

<input bindon-ngModel="currentUser.firstName">

内置指令:

  • Angular 的内置指令有 NgClass、NgStyle、NgIf、NgFor、NgSwitch等。
  • NgClass:通过绑定到 NgClass 动态添加或删除 CSS 类。
<div [style.font-size]="isSpecial ? 'x-large' : 'smaller'" >
  这个div是大号的。
</div>
  • NgStyle:NgStyle 通过把它绑定到一个key:value控制对象的形式,可以让我们同时设置很多内联样式。
setStyles() {
  let styles = {
    // CSS属性名
    'font-style':  this.canSave      ? 'italic' : 'normal',  // italic
    'font-weight': !this.isUnchanged ? 'bold'   : 'normal',  // normal
    'font-size':   this.isSpecial    ? '24px'   : '8px',     // 24px
  };
  return styles;
}
  • 通过添加一个NgStyle属性绑定,让它调用setStyles,并据此来设置元素的样式:
<div [ngStyle]="setStyles()">
    这个div的样式是italic, normal weight, 和extra large (24px)。
</div>
  • NgIf:通过把NgIf指令绑定到一个真值表达式,可以把一个元素及其子元素添加到DOM树上。
<div *ngIf="currentUser">Hello,{{currentUser.firstName}}</div>
  • 相反,绑定到一个假值表达式将从DOM树中移除该元素及其子元素。如:
<!-- 因为isActive的值为false,所以User Detail不在DOM树中-->
<user-detail *ngIf="isActive"></user-detail>
  • NgSwitch:当需要从一组可能的元素树中根据条件显示其中一个时,就需要NgSwitch了。Angular将只把选中的元素添加进DOM中。如:
<span [ngSwitch]="userName">
  <span *ngSwitchCase="'张三'">张三</span>
  <span *ngSwitchCase="'李四'">李四</span>
  <span *ngSwitchCase="'王五'">王五</span>
  <span *ngSwitchCase="'赵六'">赵六</span>
  <span *ngSwitchDefault>龙大</span>
</span>
  • 把作为父指令的NgSwitch绑定到一个能返回开关值的表达式,例子中这个值是字符串,但它可以是任何类型的值。父指令NgSwitch控制一组子元素。每个或者挂在一个匹配值的表达式上,或者被标记为默认情况。任何时候,这些span中最多只有一个会出现在DOM中。如果这个span的匹配值和开关值相等,Angular2就把这个添加DOM中。如果没有任何span匹配上,Angular2就会把默认的span添加到DOM中。Angular2会移除并销毁所有其他的span。
  • 三个相互合作的指令: ①ngSwitch:绑定到一个返回开关值的表达式 ②ngSwitchCase:绑定到一个返回匹配值的表达式 ③ngSwitchDefault:一个用于标记默认元素的属性 注意:不要再ngSwitch前使用*,而应该用属性绑定,但ngSwitchCase和ngSwitchDefault前面要放*。
  • NgFor:当需要展示一个由多个条目组成的列表时就需要这个指令了。如下面这个例子,就是在一个HTML块上应用NgFor。
<div *ngFor="let user of users">{{user.fullName}}</div>
  • NgFor也可以应用在一个组件元素上,如:
<user-detail *ngFor="let user of users" [user]="user"></user-detail>
  • ngFor指令支持一个可选的index索引,在迭代过程中会从0增长到数组中的长度。
  • 可以通过模板输入变量来捕获这个index,并应用在模板中。下面的例子就把index捕获到了一个名为i的变量中。
<div *ngFor="let user of users; let i=index">{{i + 1}} - {{user.fullName}}</div>
  • NgForTrackBy:ngFor 指令有时候会性能较差,特别是在大型列表中。 对一个条目的一点小更改、移除或添加,都会导致级联的 DOM 操作。
  • 比如,当通过重新从服务器来刷新通讯录,刷新后的列表可能包含很多(如果不是全部的话)以前显示过的联系人。但在Angular看来,它不知道哪些是以前就存在过的,只能清理旧列表、舍弃那些DOM元素,并用新的DOM元素来重建一个新列表。
  • 解决这个问题,可以通过追踪函数来避免这种折腾。追踪函数会告诉Angular:我们知道两个具有相同user.id的对象是同一个联系人。如:
trackByUsers(index: number, user: User){return user.id}
  • 然后,把NgForTrackBy指令设置为那个追踪函数:
<div *ngFor="let user of users; trackBy:trackByUsers">({{user.id}}) {{user.fullName}}</div>
  • 追踪函数不会排除所有DOM更改。如果用来判断是否为同一个联系人的属性变化了,就会更新DOM元素,反之就会留下这个DOM元素。列表界面就会变得比较更加平滑,具有更好的响应效果。

模板引用变量:

  • 模板引用变量是模板中对 DOM 元素或指令的引用。
  • 它能在原生 DOM 元素中使用,也能用于 Angular 组件——实际上,它可以和任何自定义 Web 组件协同工作。
  • 我们可以在同一元素、兄弟元素或任何子元素中引用模板引用变量。
  • 这里是关于创建和使用模板引用变量的两个例子:
<!-- phone 引用了 input 元素,并将 `value` 传递给事件句柄 -->
<input #phone placeholder="phone number">
<button (click)="callPhone(phone.value)">Call</button>
 
<!-- fax 引用了 input 元素,并将  `value` 传递给事件句柄  -->
<input ref-fax placeholder="fax number">
<button (click)="callFax(fax.value)">Fax</button>
  • "phone" 的 (#) 前缀意味着我们将要定义一个 phone 变量。

脚手架cli

前端脚手架是指什么?

  • 前端开发中提到的“脚手架”是一个形象的比喻,比喻各类语言的前期工作环境。
  • 在软件开发上(当然也包括前端开发)的脚手架指的就是:有人帮你把这个开发过程中要用到的工具、环境都配置好了,你就可以方便地直接开始做开发,专注你的业务,而不用再花时间去配置这个开发环境,这个开发环境就是脚手架。
  • 比如vue.js就有个vue-cli脚手架,基于node.js的开发环境,作者帮你把开发环境大部分东西都配置好了,你把脚手架下载下来就可以直接开发了,不用再考虑搭建这些工具环境。

Angular CLI 简介:

  • CLI是Command Line Interface的简写,是一种命令行接口,实现自动化开发流程,比如:ionic cli、vue cli等;
  • 它可以创建项目、添加文件以及执行一大堆开发任务,比如测试、打包和发布。
  • 比如集成构建中的Grunt、Gulp、webpack,测试中的ARMA、Jasmine,css编译中的Less,Sass。

断言:

  • 断言是编程术语,表示为一些布尔表达式,程序员相信在程序中的某个特定点该表达式值为真,可以在任何时候启用和禁用断言验证,因此可以在测试时启用断言而在部署时禁用断言。同样,程序投入运行后,最终用户在遇到问题时可以重新启用断言。
  • 使用断言可以创建更稳定、品质更好且不易于出错的代码。当需要在一个值为FALSE时中断当前操作的话,可以使用断言。单元测试必须使用断言(Junit/JunitX)。
  • 除了类型检查和单元测试外,断言还提供了一种确定各种特性是否在程序中得到维护的极好的方法。使用断言使我们向按契约式设计更近了一步。
  • 断言可以有两种形式: ①assert Expression1 ②assert Expression1:Expression2 <1>其中Expression1应该总是一个布尔值,Expression2是断言失败时输出的失败消息的字符串。如果Expression1为假,则抛出一个 AssertionError,这是一个错误,而不是一个异常,也就是说是一个不可控制异常(unchecked Exception),AssertionError由于是错误,所以可以不捕获,但不推荐这样做,因为那样会使你的系统进入不稳定状态。

路由Router

前端为什么需要Router?

  • 如果没有Router,浏览器的前进后退按钮没法用
  • 如果没有Router,你将无法把URL拷贝并分享给你的朋友

Angular路由用法:静态路由: 在这里插入图片描述

Angular路由用法:异步路由: 在这里插入图片描述

Angular路由用法:路由守卫:

  • 防止未授权访问 在这里插入图片描述

路由时传递数据的方式:

  • 在查询参数中传递
//传递数据
...
<a [routerLink]="['/stock']" [queryParams]="{id: 1}">股票详情</a>
// http://localhost:4200/stock?id=1



//接受参数
...
import { ActivatedRoute } from '@amgular/router';

export class StockComponent implements OnInit {

    private stockId: number;    
    
    constructor(private routeInfo: ActivatedRoute)
    
    ngOnInit() {
        this.stockId = this.routeInfo.snapshot.queryParams['id'];
    }
    
}
  • 在路由路径中传递
//修改配置
const routes: Routes = [
  {path: '', redirectTo: '/index', pathMatch: 'full'},
  {path: 'index', component: IndexComponent},
  {path: 'stock/:id', component: StocksComponent },
  {path: '**', component: ErrorPageComponent }
];


//传递数据
...
<a [routerLink]="['/stock', 1]">股票详情</a>
// http://localhost:4200/stock/1



//接受参数
...
import { ActivatedRoute } from '@amgular/router';

export class StockComponent implements OnInit {

    private stockId: number;    
    
    constructor(private routeInfo: ActivatedRoute)
    
    ngOnInit() {
        this.stockId = this.routeInfo.snapshot.params['id'];
    }
    
}
  • 使用snapshot快照的方式传递数据,因为初始化一次,路由到自身不能传递参数,需要使用订阅模式。
this.routeInfo.params.subscribe((params: Params) => this.stockId = params['id']);
  • 在路由配置中传递
//路由配置配置
const routes: Routes = [
  {path: '', redirectTo: '/index', pathMatch: 'full'},
  {path: 'index', component: IndexComponent, data: {title: 'Index Page'}},
  {path: 'stock/:id', component: StocksComponent, data: {title: 'Stock Page'}},
  {path: '**', component: ErrorPageComponent, data: {title: 'Stock Page'}}
];


//接受参数
this.title = this.routeInfo.snapshot.date[0]['title'];

UI库

UI 库Libraries:

  • rimeNG :到目前为止最完善的开源免费UI组件库
  • NG-Zorro:来自阿里云的开源组件库
  • Jigsaw:来自ZTE中兴通讯的开源组件库
  • Clarity:来自Vmware的组件库
  • Angular-Material:Angular官方提供的组件库
  • Ionic :专门为Angular设计的移动端组件库

Angular2入门之模块与组件

简介: 在这里插入图片描述

  • app文件夹下有五个文件,其中,app.component.spec.ts应该是和模块测试有关的文件,目前不用管它。剩下的四个文件就是典型的模块+组件的文件组成模式。  
  • Angular2应用由模块和组件构成,每个模块这样明明name.module.ts,组件则是name.component.ts。  
  • 这几个文件的关系是从属关系: |--app.module.ts(模块)  |--app.component.ts(组件)   |--app.component.html(HTML模板)   |--app.component.css(CSS样式表)
  • 一个模块可以有多个组件,一个组件也可以有多个样式表,但只有一个HTML模板。

组件的含义:

  • Angular2中的组件,就是我们自定义的一段HTML代码,给他取个名字,就可以当作HTML的标签使用了。假如组件的名字是my-app,那么就可以这样使用:
<div>
  <my-app><my-app>//可以像原生标签一样添加属性,而且组件能接收的属性更丰富
           //标签里面也可以放内容
</div>
  • 组件其实是一个类,它定义了一系列方法和属性,并且把HTML模板封装起来。它的意义是,你可以把它当做一个独立的隔离的盒子,并利用其中的方法和属性进行盒子内部的操作,以及和外界的父组件、兄弟组件交互。

模块的含义:

  • 模块就是将一系列的组件还有指令、服务整合封装到一起,提供一个完整的功能。模块可以被外部模块引用。

模块详解和根模块:

  • 根模块: ①每个应用会有一个根模块,按照约定,它的类名叫做AppModule,被放在app.module.ts文件中。应用启动时,就会加载这个模块。 ②每个根模块会有一个根组件,默认就是app.component.ts,名字是app-root。 ③查看项目目录中的index.html,会发现有Loading...这样的代码,就是在加载这个根模块。
  • 模块详解: ①下面是app.module.ts的代码。 ②Angular2用TypeScript组织代码的方式大致就像这个文件一样。 <1>在顶部使用import语句,导入模块或者组件需要使用的外部模块。 在模块中需要使用自己创建的组件或者其他服务、指令,也需要import。 <2>import语句之后,使用@NgModule()语句描述本模块的元数据。 1、declarations:声明属于本模块的组件,每个组件必须在且仅在一个模块中声明。 2、imports:引入买本模块中用到的模块,该模块是处于import语句引入的模块中。 3、providers:声明模块中使用的服务的提供者,暂时还没有用到。 4、bootstrap:根模块中的引导组件,应用启动过程中,会创建这个数组中的组件并插入到HTML中,一般只有一个引导组件。 <3>最后,使用export语句,创建模块的类并暴露出去。模块、组件只有暴露出去,其他的模块才能引用。
import { BrowserModule } from '@angular/platform-browser';//每个在浏览器中运行
//的应用的根模块都需要引入BrowserModule
import { NgModule } from '@angular/core';//每个模块都需要引入的核心库中的NgModule
import { FormsModule } from '@angular/forms';//表单模块,在应用中使用表单时引入
import { HttpModule } from '@angular/http';//http模块,当需要进行http远程请求时引入

import { AppComponent } from './app.component';//自己创建的组件

@NgModule({
  declarations: [
    AppComponent
  ],
  imports: [
    BrowserModule,
    FormsModule,
    HttpModule
  ],
  providers: [],
  bootstrap: [AppComponent]
})
export class AppModule { }

组件详解:

  • import语句导入需要的模块。
  • 使用@Component()来描述本组件的元数据。 ①selector:创建的组件的名字,就像HTML标签的名字一样 ②templateUrl: HTML模板,使用相对路径,./表示这个组件文件所在的当前目录, ③styleUrls:HTML模板使用的样式表,可以有多个。
  • 最后,使用export创建组件的类并暴露出去。在类中,可以创建属性和方法。
import { Component } from '@angular/core';  //所有组件必须引入

@Component({
  selector: 'app-root',
  templateUrl: './app.component.html',
  styleUrls: ['./app.component.css']
})
export class AppComponent {
  title = 'app works! 你已经完成了第一步';
}

应用启动过程:  

  • 整个应用的启动流程:加载根模块,加载所需的其他模块,创建引导组件,显示组件内容。

RxJS异步编程使用

Observable和Observer:

  • 其实,看下面这简简单单的一行代码就懂了它们的关系:
observable.subscribe(observer);
  • observable是数据源头,是生产者,是待订阅者,通过subscribe方法可以被订阅,而observer是观察者,数据使用者,数据消费者。
  • 这句代码返回的是一个订阅对象,代表着一个订阅的发生,一个订阅的过程,一个Subscription对象的实例化。
  • observer其实是一个有三个回调函数的对象,每个回调函数对应三种Observable发送的通知类型(next,error,complete)。回调函数不必每次都提供三个。如果我们只提供了一个回调函数作为参数,subscribe会将我们提供的函数参数作为next的回调处理函数。
  • observable.subscribe()是一个Subscription对象。Subscription就是表示Observable的执行,可以被清理。这个对象最常用的方法就是unsubscribe方法。同时,它还有 add方法可以使我们取消多个订阅。
  • 我有一个比喻可以很好的理解这种订阅关系:现在有一家牛奶生产商,它们家的牛奶质优价廉,鲜美醉人。它在电视上发布广告,所有人都可以打它们的电话订奶。这个时候,牛奶商就是Observable,市民就是Observer。如果市民打电话(subscribe)给牛奶商,它们就会在牛奶商送奶(next)成功的时候收到牛奶,至于怎么喝就是自己的事情了,而市民是不关心牛奶是怎么生产和如何送过来的(比如数据库,HTTP过程的TCP/IP协议,握手过程等)。送奶过程可能会遇到意外导致送奶失败(error),而成功之后,牛奶商会把这次送奶标记为已送达(complete)。

Subject:

  • Subjects 是将任意 Observable 执行共享给多个观察者的唯一方式。这段官方文档的翻译显然有些生涩。其实,简单的说,Subject既是Observable,也是观察者(可以多个)。
  • Subject 像是 Observable,但是可以多播给多个观察者。Subject 还像是 EventEmitters,维护着多个监听器的注册表。

总结:

总结

总结: