1.设置开发环境
安装Node.js和npm,全局安装Angular CLI
npm install -g @angular/cli
2.创建新项目
打开终端窗口,使用 ng add
命令往新项目中添加一些预先打包好的功能, Angular Material 就为一些典型布局提供了图纸
ng new my-app
3.启动开发服务器
监听文件变化,并在修改这些文件时重新构建此应用,在4200端口,http://localhost:4200/
.
cd my-app
ng serve --open
4.编辑Angular组件
app-root
的根组件。 你可以在 ./src/app/app.component.ts
目录下找到它。
把 title
属性从 'app'
改为 'My First Angular App!'
export class AppComponent {
title = 'My First Angular App!';
}
设计css,打开 src/app/app.component.css
并给这个组件设置一些样式
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
Angular CLI 的工作原理
src文件夹
src文件夹: Angular 组件、模板、样式、图片以及你的应用所需的任何东西
| 使用 HTML 模板、CSS 样式和单元测试定义 |
| 定义 |
| 这个文件夹下你可以放图片等任何东西,在构建应用时,它们全都会拷贝到发布包中。 |
| 这个文件夹中包括为各个目标环境准备的文件,它们导出了一些应用中要用到的配置变量。 这些文件会在构建应用时被替换。 比如你可能在生产环境中使用不同的 API 端点地址,或使用不同的统计 Token 参数。 甚至使用一些模拟服务。 所有这些,CLI 都替你考虑到了。 |
| 一个配置文件,用来在不同的前端工具之间共享目标浏览器。 |
| 每个网站都希望自己在书签栏中能好看一点。 请把它换成你自己的图标。 |
| 这是别人访问你的网站是看到的主页面的 HTML 文件。 大多数情况下你都不用编辑它。 在构建应用时,CLI 会自动把所有 |
| 给Karma的单元测试配置,当运行 |
| 这是应用的主要入口点。 使用JIT 编译器编译本应用,并启动应用的根模块 |
| 不同的浏览器对 Web 标准的支持程度也不同。 腻子脚本(polyfill)能把这些不同点进行标准化。 你只要使用 |
| 这里是你的全局样式。 大多数情况下,你会希望在组件中使用局部样式,以利于维护,不过那些会影响你整个应用的样式你还是需要集中存放在这里。 |
| 这是单元测试的主要入口点。 它有一些你不熟悉的自定义配置,不过你并不需要编辑这里的任何东西。 |
| TypeScript 编译器的配置文件。 |
| 额外的 Linting 配置。当运行 |
根目录
和src/平级
文件 | 用途 |
---|---|
| 在 |
|
|
| 给你的编辑器看的一个简单配置文件,它用来确保参与你项目的每个人都具有基本的编辑器配置。 大多数的编辑器都支持 |
| 一个 Git 的配置文件,用来确保某些自动生成的文件不会被提交到源码控制系统中。 |
| Angular CLI 的配置文件。 在这个文件中,你可以设置一系列默认值,还可以配置项目编译时要包含的那些文件。 要了解更多,请参阅它的官方文档。 |
|
|
| 给Protractor使用的端到端测试配置文件,当运行 |
| 项目的基础文档,预先写入了 CLI 命令的信息。 别忘了用项目文档改进它,以便每个查看此仓库的人都能据此构建出你的应用。 |
| TypeScript 编译器的配置,你的 IDE 会借助它来给你提供更好的帮助。 |
| 给TSLint和Codelyzer用的配置信息,当运行 |
组件是 Angular 应用中的基本构造块 ,AppComponent三个实现文件:
-
app.component.ts
— 组件的类代码,这是用 TypeScript 写的。 -
app.component.html
— 组件的模板,这是用 HTML 写的。 -
app.component.css
— 组件的私有 CSS 样式。
修改应用标题
title = 'My First Project';/*在app.component.ts*/
<h1>{{title}}</h1>/*在src/app/app.component.html,双花括号是插值绑定语法*/
添加应用样式
/* Application-wide Styles */
h1 {
color: #369;
font-family: Arial, Helvetica, sans-serif;
font-size: 250%;
}
h2, h3 {
color: #444;
font-family: Arial, Helvetica, sans-serif;
font-weight: lighter;
}
body {
margin: 2em;
}
body, input[text], button {
color: #888;
font-family: Cambria, Georgia;
}
/* everywhere else */
* {
font-family: Arial, Helvetica, sans-serif;
}
src/app/app.component.ts
import { Component } from '@angular/core';
@Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Tour of Heroes';
}
import { Component, OnInit } from '@angular/core';
/*导入 Component 符号,并为组件类加上 @Component 装饰器*/
@Component({
selector: 'app-heroes',//组件的选择器(CSS 元素选择器)
templateUrl: './heroes.component.html',//组件模板文件的位置
styleUrls: ['./heroes.component.css']//组件私有 CSS 样式表文件的位置
})
export class HeroesComponent implements OnInit {
constructor() { }
ngOnInit() {// a lifecycle hook ,创建完组件后很快就会调用 ngOnInit,放置初始化逻辑的地方
}
}
添加 hero
属性
hero = 'Windstorm';//heroes.component.ts中添加一个 hero 属性
{{hero}}// heroes.component.html
显示 HeroesComponent
视图
HeroesComponent
加到AppComponent
的模板
src/app/app.component.html
<h1>{{title}}</h1>
<app-heroes></app-heroes>
src/app/hero.ts
export class Hero {
id: number;
name: string;//添加 id 和 name 属性
}
src/app/heroes/heroes.component.ts
import { Component, OnInit } from '@angular/core';
import { Hero } from '../hero';
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
export class HeroesComponent implements OnInit {
hero: Hero = {
id: 1,
name: 'Windstorm'
};
constructor() { }
ngOnInit() {
}
}
显示 hero
对象
heroes.component.html
<h2>{{ hero.name }} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div><span>name: </span>{{hero.name}}</div>
使用 UppercasePipe
进行格式化
<h2>{{ hero.name | uppercase }} Details</h2>//显示成了大写字母
pipe operator ( | )
pipe是格式化字符串、金额、日期和其它显示数据的好办法
数据流从组件类流出到屏幕,并且从屏幕流回到组件类
**数据流动自动化,双向数据绑定:[(ngModel)] 是 Angular 的双向数据绑定语法,记得添加此模块FormsModule
<div>
<label>name:
<input [(ngModel)]="hero.name" placeholder="name">
</label>
</div>
元数据(metadata)、顶级类 AppModule
app.module.ts (FormsModule symbol import)
//从 @angular/forms 库中导入 FormsModule 符号
import { FormsModule } from '@angular/forms'; // <-- NgModel lives here
//把 FormsModule 添加到 @NgModule 元数据的 imports 数组中,这里是该应用所需外部模块的列表
app.module.ts ( @NgModule imports)
imports: [
BrowserModule,
FormsModule
],
声明 HeroesComponent
每个组件都必须声明在(且只能声明在)一个 NgModule 中。
创建模拟(mock)的数据
src/app/
文件夹中创建一个名叫 mock-heroes.ts
的文件
src/app/mock-heroes.ts
import { Hero } from './hero';
export const HEROES: Hero[] = [
{ id: 11, name: 'Mr. Nice' },
{ id: 12, name: 'Narco' },
{ id: 13, name: 'Bombasto' },
{ id: 14, name: 'Celeritas' },
{ id: 15, name: 'Magneta' },
{ id: 16, name: 'RubberMan' },
{ id: 17, name: 'Dynama' },
{ id: 18, name: 'Dr IQ' },
{ id: 19, name: 'Magma' },
{ id: 20, name: 'Tornado' }
];
显示数据
src/app/heroes/heroes.component.ts (import HEROES)
import { HEROES } from '../mock-heroes';
heroes = HEROES;
使用 *ngFor
列出
heroes.component.html (heroes template)
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<!--*ngFor 是一个 Angular 的复写器(repeater)指令。 它会为列表中的每项数据复写它的宿主元素。heroes 就是来自 HeroesComponent 类的列表-->
@Component.styles、@Component.styleUrls
所指出的样式表文件中
src/app/heroes/heroes.component.ts (@Component)
@Component({
selector: 'app-heroes',
templateUrl: './heroes.component.html',
styleUrls: ['./heroes.component.css']
})
/*
@Component 元数据中指定的样式和样式表都是局限于该组件的。
heroes.component.css 中的样式只会作用于 HeroesComponent,
既不会影响到组件外的 HTML,也不会影响到其它组件中的 HTML。
*/
主从结构
监听点击事件,添加 click
事件绑定
heroes.component.html (template excerpt)
<li *ngFor="let hero of heroes" (click)="onSelect(hero)">
添加 click
事件处理器
src/app/heroes/heroes.component.ts (onSelect)
selectedHero: Hero;
onSelect(hero: Hero): void {
this.selectedHero = hero;
}
修改详情模板
把 hero
改名为 selectedHero
heroes.component.html (selected hero details)
<h2>{{ selectedHero.name | uppercase }} Details</h2>
<div><span>id: </span>{{selectedHero.id}}</div>
<div>
<label>name:
<input [(ngModel)]="selectedHero.name" placeholder="name">
</label>
</div>
使用 *ngIf
隐藏空白的详情
把显示数据详情的 HTML 包裹在一个 <div>
中。 并且为这个 div 添加 Angular 的 *ngIf
指令,把它的值设置为 selectedHero
。
src/app/heroes/heroes.component.html (*ngIf)
<div *ngIf="selectedHero">
<h2>{{ selectedHero.name | uppercase }} Details</h2>
<div><span>id: </span>{{selectedHero.id}}</div>
<div>
<label>name:
<input [(ngModel)]="selectedHero.name" placeholder="name">
</label>
</div>
</div>
CSS 类绑定机制
[class.some-css-class]="some-condition"
添加到你要施加样式的元素
heroes.component.html (toggle the 'selected' CSS class)
/*如果当前行的英雄和 selectedHero 相同,Angular 就会添加 CSS 类 selected,否则就会移除它*/
<li *ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
主从组件
把大型组件拆分成小一点的子组件
ng generate component hero-detail
src/app/hero-detail/hero-detail.component.html
<div *ngIf="hero">
<h2>{{ hero.name | uppercase }} Details</h2>
<div><span>id: </span>{{hero.id}}</div>
<div>
<label>name:
<input [(ngModel)]="hero.name" placeholder="name"/>
</label>
</div>
</div>
添加 @Input() hero
属性
@Input是用来定义模块的输入的,用来让父模块往子模块传递内容,父模块在引用子模块的时候是用的[]
src/app/hero-detail/hero-detail.component.ts (import Hero)
import { Hero } from '../hero';
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
//修改 @angular/core 的导入语句,导入 Input 符号
import { Component, OnInit, Input } from '@angular/core';
添加一个带有 @Input() 装饰器的 hero 属性。
@Input() hero: Hero;
heroes.component.html (HeroDetail binding)
<!-- HeroesComponent.selectedHero 绑定到该元素的 hero 属性-->
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
单向数据绑定
<h2>My Heroes</h2>
<ul class="heroes">
<li *ngFor="let hero of heroes"
[class.selected]="hero === selectedHero"
(click)="onSelect(hero)">
<span class="badge">{{hero.id}}</span> {{hero.name}}
</li>
</ul>
<app-hero-detail [hero]="selectedHero"></app-hero-detail>
创建服务
Angular 的依赖注入机制把它注入到 HeroesComponent
的构造函数中
将创建一个 MessageService
,并且把它注入到两个地方:
-
HeroService
中,它会使用该服务发送消息。 -
MessagesComponent
中,它会显示其中的消息。
使用 Angular CLI 创建一个名叫 hero
的服务。
ng generate service hero
src/app/hero.service.ts (new service)
import { Injectable } from '@angular/core';
@Injectable({//@Injectable()装饰器,依赖注入系统的参与者
providedIn: 'root',
})
export class HeroService {//提供一个可注入的服务
constructor() { }
}
HeroService
可以从任何地方获取数据:Web 服务、本地存储(LocalStorage)或一个模拟的数据源。
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
getHeroes(): Hero[] {//让它返回模拟列表
return HEROES;
}
在将Service注入到Component之前,需要将service提供给依赖注入系统。
通过 注册provider,创建和交付(deliver)service
用注册器(injector)注册它,injector是一个对象
默认情况下,Angular CLI 命令 ng generate service
会通过给 @Injectable
装饰器添加元数据的形式,
为该服务把提供商注册到根注入器上。可在不同层次上注册provider
自动在module层上提供服务
ng generate service hero --module=app
src/app/hero.service.ts
import { Injectable } from '@angular/core';
import { Observable, of } from 'rxjs';
import { Hero } from './hero';
import { HEROES } from './mock-heroes';
import { MessageService } from './message.service';
@Injectable({
providedIn: 'root',
})
export class HeroService {
constructor(private messageService: MessageService) { }
/*1. 声明了一个私有 Service 属性
2. 把它标记为一个Service 的注入点。
设置成单例对象*/
getHeroes(): Observable<Hero[]> {//获取数据
// TODO: send the message _after_ fetching the heroes
this.messageService.add('HeroService: fetched heroes');
return of(HEROES);
}
}
src/app/message.service.ts
import { Injectable } from '@angular/core';
@Injectable({
providedIn: 'root',
})
export class MessageService {
messages: string[] = [];
add(message: string) {//添加一条消息
this.messages.push(message);
}
clear() {//清空缓存
this.messages = [];
}
}
在component中调用
ngOnInit() {
this.getHeroes();
}
- 将数据访问逻辑重构到Service 类中
- 根注入器中把
Service
注册为该服务的提供商,以便在别处可以注入它 - 使用Angular依赖注入机制注入组件
- 给
Service
中获取数据的方法提供了一个异步的函数签名
HeroService.getHeroes()
必须具有某种形式的异步函数签名。HeroService.getHeroes()
将会HttpClient.get()
返回 Observable,
Observable
以及 RxJS 库
src/app/hero.service.ts (Observable imports)
import { Observable, of } from 'rxjs';
- 使用 RxJS 的
of()
方法返回了一个模拟英雄数据的可观察对象 (Observable<Hero[]>
)
getHeroes(): void {
this.heroService.getHeroes()
.subscribe(heroes => this.heroes = heroes);
}
Observable.subscribe()
是关键的差异点。 subscribe()函数
这个英雄数组传给这个回调函数,该函数把英雄数组赋值给组件的 heroes
属性。
使用这种异步方式,当 HeroService
从远端服务器获取英雄数据时,就可以工作了。
HeroService显示消息:打开MessagesComponent
可以显示所有消息,导入MessageService,在constructor中注入
-
*ngIf
只有当在有消息时才会显示消息区。 -
*ngFor
用来在一系列<div>
元素中展示消息列表。 -
Angular 的事件绑定把按钮的
click
事件绑定到了MessageService.clear()
。
- 在组件的
ngOnInit
生命周期钩子中调用HeroService
方法,而不是构造函数中 - 你创建了一个
MessageService
,以便在类之间实现松耦合通讯 HeroService
连同注入到它的服务MessageService
一起,注入到了组件中