Angular入门

Angular由工程师组织打造,我们拥有共同的热情 —— 让Web开发变得更简单。我们深信,写漂亮的程序快乐而有趣。 我们正在构建面向未来的平台。在进行各种取舍之后,最终选择了Angular,最新版本为4.2,本篇是入门篇。

Quickstart

安装node.js: https://nodejs.org/en/

将npm改成淘宝国内源,以后所有npm命令用cnpm代替

1
npm install -g cnpm --registry=https://registry.npm.taobao.org

然后初始化quickstart项目(我将它重命名为xn-angular):

1
2
3
git clone https://github.com/angular/quickstart.git xn-angular
cd xn-angular
cnpm install

删除非必需文件,在windows使用git bash来执行

OS/X (bash)

1
2
3
xargs rm -rf < non-essential-files.osx.txt
rm src/app/*.spec*.ts
rm non-essential-files.osx.txt

然后启动服务器,以后更新所有代码都不需要刷新页面,也不需要重启服务器,自动发现并重载:

1
cnpm start

看到下面的欢迎页面就说明起步成功:

Angular构造块

下面这个图是Angular的全景图:

要理解Angular的架构,需要理清8个构造块的含义

  • 模块 (module) - (无论是根模块还是特性模块)都是一个带有@NgModule装饰器的类。
  • 组件 (component) - 组件负责控制屏幕上的一小块区域(视图)
  • 模板 (template) - 通过组件的自带的模板来定义组件视图。模板以HTML形式存在,告诉Angular如何渲染组件。
  • 元数据 (metadata) - 元数据告诉 Angular 如何处理一个类。一般通过装饰器 (decorator) 来附加元数据
  • 数据绑定 (data binding) - 让模板的各部分与组件的各部分相互合作的机制
  • 指令 (directive) - Angular模板是动态的。当Angular渲染它们时,它会根据指令提供的操作对 DOM 进行转换
  • 服务 (service) - 几乎任何东西都可以是一个服务。典型的服务是一个类,具有专注的、明确的用途
  • 依赖注入 (dependency injection) - “依赖注入”是提供类的新实例的一种方式,还负责处理好类所需的全部依赖。 大多数依赖都是服务。Angular使用依赖注入来提供新组件以及组件所需的服务。

英雄项目示例

这个是官网上面的教程,一步步来做即可

Form表单

官网开发指南,演示效果

模板绑定

HTML attributeDOM property 的对比

要想理解 Angular 绑定如何工作,重点是搞清 HTML attributeDOM property 之间的区别。 attribute 是由 HTML 定义的。property 是由 DOM (Document Object Model)` 定义的。

  • 少量 HTML attributeproperty 之间有着 1:1 的映射,如id。
  • 有些 HTML attribute 没有对应的 property,如colspan
  • 有些 DOM property 没有对应的 attribute,如textContent
  • 大量 HTML attribute看起来映射到了property…… 但却不像我们想的那样!

最后一类尤其让人困惑…… 除非我们能理解这个普遍原则:

attribute 初始化 DOM property,然后它们的任务就完成了。property 的值可以改变;attribute 的值不能改变。

例如,当浏览器渲染<input type="text" value="Bob">时,它将创建相应 DOM 节点, 其value property 被初始化为 “Bob”。

当用户在输入框中输入Sally时,DOM 元素的value property 变成了Sally。 但是这个 HTML value attribute 保持不变。如果我们读取 input 元素的 attribute, 就会发现确实没变:input.getAttribute('value') // 返回 “Bob”。

HTML attribute value指定了初始值;DOM value property 是当前值。

这句话值得再强调一次:模板绑定是通过 property 和事件来工作的,而不是 attribute

双向绑定

sizer.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import {Component, EventEmitter, Input, Output} from '@angular/core';
@Component({
selector: 'my-sizer',
template: `
<div>
<button (click)="dec()" title="smaller">-</button>
<button (click)="inc()" title="bigger">+</button>
<label [style.font-size.px]="size">FontSize: {{size}}px</label>
</div>`
})
export class SizerComponent {
@Input() size: number | string;
@Output() sizeChange = new EventEmitter<number>();

dec() {
this.resize(-1);
}

inc() {
this.resize(+1);
}

resize(delta: number) {
this.size = Math.min(40, Math.max(8, +this.size + delta));
this.sizeChange.emit(this.size);
}
}
app.component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import {Component} from '@angular/core';

@Component({
selector: 'my-app',
template: `
<h1>Hello {{name}}</h1>

<my-sizer [(size)]="fontSizePx"></my-sizer>
<div [style.font-size.px]="fontSizePx">Resizable Text</div>
`,
})
export class AppComponent {
name = 'Angular';
fontSizePx = 10;
}
app.module.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import { NgModule }      from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { FormsModule } from '@angular/forms'; // <--- JavaScript import from Angular

import { AppComponent } from './app.component';
import { SizerComponent } from './sizer.component';

@NgModule({
imports: [
BrowserModule,
FormsModule // <--- import into the NgModule
],
declarations: [ AppComponent, SizerComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }

size的初始值是一个输入值,来自属性绑定。(注意size前面的@Input) 点击按钮, 在最小/最大值范围限制内增加或者减少size。 然后用调整后的size触发sizeChange事件。

SizerComponent.size初始值是AppComponent.fontSizePx。 点击按钮时,通过双向绑定更新AppComponent.fontSizePx。 被修改的AppComponent.fontSizePx通过样式绑定,改变文本的显示大小。

双向绑定语法实际上是属性绑定和事件绑定的语法糖。 Angular将SizerComponent的绑定分解成这样:

1
<my-sizer [size]="fontSizePx" (sizeChange)="fontSizePx=$event"></my-sizer>

$event变量包含了SizerComponent.sizeChange事件的荷载。 当用户点击按钮时, Angular将$event赋值给AppComponent.fontSizePx

显然,比起单独绑定属性和事件,双向数据绑定语法显得非常方便。

表单元素绑定

使用[(ngModel)]进行双向绑定。

1
<input [(ngModel)]="currentHero.name">

等价于:

1
2
3
<input
[ngModel]="currentHero.name"
(ngModelChange)="currentHero.name=$event">

我们自己写的Angular组件不需要值访问器,因为我们可以让值和事件的属性名适应Angular基本的双向绑定语法,而不使用NgModel。 前面看过的sizer就是使用这种技巧的例子。

下面这个生造的例子强制输入框的内容变成大写:

1
2
3
<input
[ngModel]="currentHero.name"
(ngModelChange)="setUppercaseName($event)">

结构性指令

通过把NgIf指令应用到元素上(称为宿主元素),我们可以往DOM中添加或从DOM中移除这个元素。

1
<hero-detail *ngIf="isActive"></hero-detail>

注意:结构性指令前面要加一个星星“*”

NgFor是一个重复器指令,最常见的就for循环了:

1
2
<div *ngFor="let hero of heroes">{{hero.name}}</div>
<div *ngFor="let hero of heroes; let i=index">{{i + 1}} - {{hero.name}}</div>

要学习更多的类似 index 的值,例如lastevenodd, 请参阅 NgFor API

也可以把NgFor应用在一个组件元素上,就下例这样:

1
<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>

模板输入变量,将hero输入到组件的hero属性

1
2
<div *ngFor="let hero of heroes">{{hero.name}}</div>
<hero-detail *ngFor="let hero of heroes" [hero]="hero"></hero-detail>

NgSwitch实际上包括三个相互协作的指令:NgSwitchNgSwitchCaseNgSwitchDefault,例子如下:

1
2
3
4
5
6
7
<div [ngSwitch]="currentHero.emotion">
<happy-hero *ngSwitchCase="'happy'" [hero]="currentHero"></happy-hero>
<sad-hero *ngSwitchCase="'sad'" [hero]="currentHero"></sad-hero>
<!-- 原生的HTML元素也能用 -->
<div *ngSwitchCase="'confused'">Are you as confused as {{currentHero.name}}?</div>
<unknown-hero *ngSwitchDefault [hero]="currentHero"></unknown-hero>
</div>

如果没有合适的宿主元素的时候,可以使用<ng-container>包含,它不生成任何DOM元素:

1
2
3
4
5
6
7
<select [(ngModel)]="hero">
<ng-container *ngFor="let h of heroes">
<ng-container *ngIf="showSad || h.emotion !== 'sad'">
<option [ngValue]="h">{{h.name}} ({{h.emotion}})</option>
</ng-container>
</ng-container>
</select>

模板引用变量

模板引用变量通常用来引用模板中的某个DOM元素,它还可以引用Angular组件或指令或Web Component

1
2
3
4
5
6
<input #phone placeholder="phone number">

<!-- lots of other elements -->

<!-- phone refers to the input element; pass its `value` to an event handler -->
<button (click)="callPhone(phone.value)">Call</button>

题外话

其实这个Angular主要是靠动手去试试试,实践出真理。在项目里面用有问题去官网查,去看书,去google。 另外我买了本《Angular权威指南》的书很不错值得推荐。