Angular Intro
@LeeGenD
基于TypeScript
Angular是什么
- 一个框架
- 让构建web应用更加简单的
- 包含申明性模版、依赖注入等功能

Angular有什么
- 指令、组件、数据绑定、服务、管道、模块

对比AngularJS
- TypeScript,类型检查、方法调用等检测
- 性能,渲染速度更快
- 懒加载,初始资源加载少
基础

指令(Directive)
- 通过指令这一标记,给dom添加特定的自定义功能,如样式变化、结构调整、事件暴露等
使用方法
import {Directive} from '@angular/core';
@Directive({
selector: 'my-directive',// 选择器名.class/[attr]/div
})
export class MyDirective { }
指令类型
- 属性型指令:改变被选择的元素的属性(外观、行为)
- 结构型指令:改变被选择的元素的结构(DOM布局)
- 属性型指令:改变被选择的元素的属性
结构型指令:改变被选择的元素的结构
<p glory="yellow">Knight</p>
<p glory="black">Kngiht</p>


属性型指令:改变被选择的元素的属性- 结构型指令:改变被选择的元素的结构
<p *ngIf="true">Knight</p>
<p *ngIf="false">Kngiht</p>
Knight


- 属性型指令:NgStyle...
- 结构型指令:NgIf...

- 属性型指令:NgStyle,NgClass,NgModel,Click...
- 结构型指令:NgIf,NgFor,NgSwitch,NgTemplate...

Ng家族
组件(Component)
- 一种特殊的指令,拥有模版和样式
使用方法
import {Component} from '@angular/core';
@Component({
/* 和directive相同的属性 */
selector: 'my-component',
styles: ['p {color:white;}'],
template: '<p>Hello World</p>'
})
export class MyComponent { }
<!-- 调用bat-man组件 -->
<bat-man></bat-man>
import {Component} from '@angular/core';
@Component({
/* 和directive相同的属性 */
selector: 'bat-man',
styles: ['/** 一顿乱调 */'],
template: `
<div bat-suit>
<header>Bruce Wayne</header>
<div>body</div>
<footer>foot</footer>
</div>
`
})
export class MyComponent { }
<!-- 调用bat-man组件 -->
<bat-man></bat-man>
转换
import {Component} from '@angular/core';
@Component({
/* 和directive相同的属性 */
selector: 'bat-man',
styles: ['/** 一顿乱调 */'],
template: `
<div bat-suit>
<header>Bruce Wayne</header>
<div>body</div>
<footer>foot</footer>
</div>
`
})
export class MyComponent { }

<!-- 调用bat-man组件 -->
<bat-man></bat-man>

转换
生成
加特效
模板
import {Component} from '@angular/core';
@Component({
selector: 'my-component',
template: `
<h1>Secret Information</h1>
<p *ngIf="isShow">It's *** who have done this to you!</p>
<ng-content></ng-content> <!-- 内容投影 -->
`,
// templateUrl: './my-component.component.html' // 外部引用方式
})
export class MyComponent { }
- 原生DOM
- Angular指令
- ng-content(内容投影)
<!-- my-component.template -->
<h1>Secret Information</h1>
<p *ngIf="isShow">It's *** who have done this to you!</p>
<ng-content></ng-content> <!-- 内容投影 -->
ng-content
<my-component>Hello World</my-component>
<!-- my-component.template最终生成 -->
<h1>Secret Information</h1>
<p *ngIf="isShow">It's *** who have done this to you!</p>
Hello World <!-- 内容投影被替换 -->
生命周期
初始化数据
构建content元素(内容投影)
构建view元素(template)
生命周期钩子
class ParentElementComponent {
constructor() {}
ngOnChanges(changes: any) {}// 当被绑定的输入属性(@Input)的值发生变化时调用
ngOnInit() {}// 初始化指令/组件,仅一次(重要!)
ngDoCheck() {}// 在每个Angular变更检测周期中调用
ngAfterContentInit() {}// 当把内容投影进组件之后调用,仅一次
ngAfterContentChecked() {}// 检测内容投影的变化后调用
ngAfterViewInit() {}// 初始化完组件视图及其子视图之后调用,仅一次
ngAfterViewChecked() {}// 检测视图及其子视图的变化后调用
ngOnDestroy() {}// 销毁时调用
}
组件样式_添加方法
// 模板内联
@Component({
selector: 'hero-app',
template: `
<h1>Tour of Heroes</h1>
<style>
span { border: 1px solid black;}
</style>`
})
// 设置styles或styleUrls元数据
@Component({
selector: 'hero-app',
template: `
<h1>Tour of Heroes</h1>
`,
styles: ['h1 { font-weight: normal; }'],
styleUrls: ['app/style.css']
})
组件样式_作用域
- 默认只作用到当前组件下
- 默认不作用到外部元素和子组件下
- 默认通过属性名的方式产生作用

组件样式_特殊选择器
- :host 直接作用于宿主元素
- :host-context 宿主上下文选择器
- /deep/ 深作用,让样式可以作用于子组件
// 宿主上下文选择器
// 若宿主元素的祖先元素中存在类名为theme-light的元素,则生效
:host-context(.theme-light) h2 {
background-color: #eef;
}
<issue-card></issue-card>
<p shiny-text>It's been a long day.</p>
<div class="image-uploader"></div>
<div *ngDouble></div>
what?
<issue-card></issue-card> <!-- Component -->
<p shiny-text>It's been a long day.</p> <!-- Directive -->
<div class="image-uploader"></div> <!-- Component -->
<div *ngDouble></div> <!-- Directive -->
what?


where?


where?
- 复用性
- 逻辑分离
数据绑定(Data Binding)
- 组件内数据和模板内数据互相传播
简介
import { Component,
Input } from "@angular/core";
@Component({
template: `
<input [value]="inputVal" />
`
})
class InputComponent {
@Input() inputVal = 'init value';
}

使用方法
<!-- 插值表达式 -->
<p>{{ name }}</p>
<!-- 属性绑定 -->
<input [value]="firstName">
<!-- 事件绑定 -->
<li (click)="selectHero(hero)"></li>
双向数据绑定
// 双向绑定内幕
@Component({
template: `
<input [value]="inputVal" (input)="valueChange($event)" />
`
})
class InputComponent {
inputVal = 'init value';
valueChange($event) {
this.inputVal = $event.target.value;
}
}
双向数据绑定
// 双向绑定内幕
@Component({
template: `
<input
[ngModel]="inputVal"
(ngModelChange)="inputVal=$event">
`
})
class InputComponent {
inputVal = 'init value';
}
ngModel指令通过自己的输入属性ngModel和输出属性ngModelChange隐藏了那些细节。
服务(Service)
- 一个广义范畴,包括:值、函数,或应用所需的特性
- 具有专注的、明确的用途
使用方法
// 申明LogService服务
@Injectable()
export class LogService {
info(): void {}
error(): void {}
}
使用方法
// 注入LogService
import {Component} from '@angular/core';
@Component({
template: '<p>Hello World</p>'
})
export class MyComponent {
//1. 在构建函数中注入后,Angular会将其实例化
constructor(private logService: LogService) {}
//2. 调用LogService的方法
print() {
this.logService.info('hello world');
}
}
举例
// 浏览器地址相关
import { Location } from '@angular/common';
// loading框
import { LoadingController } from 'ionic-angular';
// 弹框
import { AlertController } from 'ionic-angular';


print?
export class TestService {
private count = 0;
add() {
this.count++;
}
print() {
console.log(`TestService print ${this.count}`);
}
}
@Component({
selector: 'tomato-item',
providers: []
})
export class TomatoItemComponent {
constructor(private test: TestService) {
this.test.add();
this.test.print();
}
}
@Component({
selector: 'tomato-item',
providers: [TestService]
})
export class TomatoItemComponent {
constructor(private test: TestService) {
this.test.add();
this.test.print();
}
}
<tomato-item></tomato-item>
<tomato-item></tomato-item>
<tomato-item></tomato-item>
print?
export class TestService {
private count = 0;
add() {
this.count++;
}
print() {
console.log(`TestService print ${this.count}`);
}
}
@Component({
selector: 'tomato-item',
providers: []
})
export class TomatoItemComponent {
constructor(private test: TestService) {
this.test.add();
this.test.print();
}
}
@Component({
selector: 'tomato-item',
providers: [TestService]
})
export class TomatoItemComponent {
constructor(private test: TestService) {
this.test.add();
this.test.print();
}
}
<tomato-item></tomato-item>
<tomato-item></tomato-item>
<tomato-item></tomato-item>


print?
@Component({
selector: 'tomato-item',
providers: []
})
export class TomatoItemComponent {
constructor(private test: TestService) {
this.test.add();
this.test.print();
}
}
@Component({
selector: 'tomato-item',
providers: [TestService]
})
export class TomatoItemComponent {
constructor(private test: TestService) {
this.test.add();
this.test.print();
}
}
第一次注入TestService时实例化,之后每次注入都会使用同一个TestService
每一次注入TestService都会产生新的实例
管道(Pipe)
- 管道把数据作为输入,然后转换它,给出期望的输出
内置的管道
- DatePipe、UpperCasePipe、LowerCasePipe、CurrencyPipe和PercentPipe
参数化
<!-- 这段代码运行于2017年7月30日 -->
<p>{{ now | date }}</p>
<p>{{ now | date: "MM/dd/yyyy" }}</p>
<p>{{ now | date: "MM/dd" }}</p>
07/30/2017
07/30
Jul 30, 2017
链式
<!-- 这段代码运行于2017年7月30日 -->
<p>{{ now | date | uppercase }}</p>
Jul 30, 2017
Sun Jul 30 2017 11:03:24...
JUL 30, 2017
date
uppercase
模块(Module)
- 帮你把应用组织成多个内聚的功能块
Service:公共服务
Directive:指令
Component:组件
Pipe:管道
子模块
NgModule
import { NgModule } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
@NgModule({
// 引入其它子模块
imports: [ BrowserModule ],
// Service提供商
providers: [ Logger ],
// 声明Component,Directive,Pipe
declarations: [ AppComponent,
HighLightDirective,
TipPipe ],
// 导出
exports: [ AppComponent ],
bootstrap: [ AppComponent ]
})
export class AppModule { }
懒加载
import { NgModule } from '@angular/core';
import { Routes, RouterModule } from '@angular/router';
export const routes: Routes = [
{ path: 'crisis', loadChildren: 'app/crisis/crisis.module#CrisisModule' },
{ path: 'heroes', loadChildren: 'app/hero/hero.module#HeroModule' }
];
@NgModule({
imports: [RouterModule.forRoot(routes)],
exports: [RouterModule]
})
export class AppRoutingModule {}
Todo

- 顶部为添加模块,图片链接为网络链接
- 添加条目后初始化分数为100%
- 点击Good或Bad按钮,将操作记录下来,更新新的新鲜率
Todo

- 若新鲜率低于70%,显示红色,高于90%,显示绿色
- 数据存储在localStorage
- 举例:点Good3次,Bad1次后,新鲜率为75%
Best Practice

TomatoAddComponent
TomatoItemComponent
TomatoService
add()
getAll()
update()
The End-Q&A

Angular Introduce 2.0
By leegend
Angular Introduce 2.0
- 198