由于未来项目需要(PM说的),于是展开了一个新的技能树,在此记录Angular入门学习过程。今天从0开始接触。

话外:

AngularJS和Angular的区别

老的angular也就是1.x版本的称为AngularJS,新的2.x以上的版本重命名为Angular。所以肯定上手新的Angular。

开始安装

每个框架一般都有自己的脚手架,方便进行开发,使开发人员专注于开发,而不花费时间在环境配置上。

Angular CLI就是Angular的脚手架。

全局安装Angular CLI(前提是已经安装node.js)

1
npm install -g @angular/cli

创建一个新的、基本的 Angular 项目

1
2
3
ng new my-first-project      #新建名为my-first-project项目
cd my-first-project #打开my-first-project
ng serve -o #启动项目并从默认浏览器打开

开始学习

根据官方例子进行学习

一、模版语法

1、通过*ngFor进行遍历操作,并用插值语法 {{}}进行显示。

1
2
3
4
5
6
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
{ { product.name }}
</h3>
</div>

2、鼠标悬停预览详情

1
2
3
4
5
6
7
8
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'">
{ { product.name }}
</a>
</h3>
</div>

3、添加商品说明。在< p >标签上,用*ngIf指令,这样 Angular 只会在当前商品有描述信息的情况下创建这个< p >元素。

1
2
3
4
5
6
7
8
9
10
11
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'">
{ { product.name }}
</a>
</h3>
<p *ngIf="product.description">
Description: { { product.description }}
</p>
</div>

4、添加一个按钮,以便让用户可与朋友分享商品。

把button的click事件绑定到我们替你定义好的share()方法上(位于product-list.component.ts)。

事件绑定是通过把事件名称包裹在圆括号()中完成的。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'">
{ { product.name }}
</a>
</h3>
<p *ngIf="product.description">
Description: { { product.description }}
</p>
<button (click)="share()">
Share
</button>
</div>

小结:

该应用现在具有商品列表和共享功能。Angular 模板语法的五个常用特性:

  • *ngFor

  • *ngIf

  • 插值 {{}}

  • 属性绑定 []

  • 事件绑定()

二、组件 

  • app-root(橙色框)是应用的外壳。这是第一个组件,也是所有其它组件的父组件。可以把它想象成一个基础页面。

  • app-top-bar(蓝色背景)是商店名称和结帐按钮。

  • app-product-list(紫色框)是你在上一节中修改过的商品列表。

1、创建一个新组件:product-alerts,包含三个主要文件:样式文件css,模版文件html,类文件ts。

1
ng generate component product-alerts

用命令生成的文件都是有默认模版的,规定以下带注释的都是后添加的。 

文件product-alerts.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
import { Component, OnInit } from '@angular/core';

//从angular导入Input装饰器
import { Input } from '@angular/core';
//从angular导入Ouput装饰器、EventEmitter事件发射器
import { Output, EventEmitter } from '@angular/core';

@Component({
selector: 'app-product-alerts',
templateUrl: './product-alerts.component.html',
styleUrls: ['./product-alerts.component.css']
})
export class ProductAlertsComponent implements OnInit {

//定义一个带 @Input() 装饰器的 product 属性。
//@Input() 装饰器指出其属性值是从该组件的父组件商品列表组件中传入的。
@Input() product;

//用 @Output() 装饰器和一个事件发射器 EventEmitter() 实例定义一个名为 notify 的属性。
//这可以让商品提醒组件在 notify 属性发生变化时发出事件。
@Output() notify = new EventEmitter();

constructor() { }
ngOnInit() {
}
}

文件product-alerts.component.html

1
2
3
4
5
<!-- 如果商品价格超过 700 美元就要显示出来的“通知我”按钮。 -->
<p *ngIf="product.price > 700">
<!-- 用事件绑定更新“Notify Me”按钮,以调用 notify.emit() 方法。 -->
<button (click)="notify.emit()">Notify Me</button>
</p>

文件product-list.component.ts

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { Component } from '@angular/core';
import { products } from '../products';

@Component({
selector: 'app-product-list',
templateUrl: './product-list.component.html',
styleUrls: ['./product-list.component.css']
})
export class ProductListComponent {
products = products;

share() {
window.alert('The product has been shared!');
}

//应该由父组件(商品列表组件)采取行动,而不是商品提醒组件。
onNotify() {
window.alert('You will be notified when the product goes on sale');
}
}

将product-alert作为组件写入product-list

文件product-list.component.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<h2>Products</h2>
<div *ngFor="let product of products">
<h3>
<a [title]="product.name + ' details'">
{ { product.name }}
</a>
</h3>
<p *ngIf="product.description">
Description: { { product.description }}
</p>
<button (click)="share()">
Share
</button>

<!-- product-alert组件 -->
<!-- 修改商品列表组件以接收商品提醒组件的输出。 -->
<app-product-alerts
[product]="product"
(notify)="onNotify()">
</app-product-alerts>
</div>

三、路由 

将商品详情建立为组件,通过路由跳转访问详情。

注册路由

文件app.module.ts

1
2
3
4
5
6
7
8
9
10
11
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: '', component: ProductListComponent },

//添加一个商品详情路由,该路由的 path 是 products/:productId
{ path: 'products/:productId', component: ProductDetailsComponent },
])
],

文件product-list.component.html

1
2
3
4
5
6
7
8
9
<!-- 把商品索引赋值给productId -->
<div *ngFor="let product of products; index as productId">
<h3>
<!-- 设置路由链接 -->
<a [title]="product.name + ' details'" [routerLink]="['/products', productId]">
{ { product.name }}
</a>
</h3>
</div>

新建商品详情组件

1
ng generate component product-details

文件product-details.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
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
//从 ../products 文件导入 products 数组
import { products } from "../products";

@Component({
selector: "app-product-details",
templateUrl: "./product-details.component.html",
styleUrls: ["./product-details.component.css"]
})
export class ProductDetailsComponent implements OnInit {
//定义 product 属性,并把它加入构造函数括号中作为参数,以便把 ActivatedRoute 注入到构造函数中
product;
constructor(
private route: ActivatedRoute,
) {}

//在 ngOnInit() 方法中订阅了路由参数,并且根据 productId 获取了该产品
ngOnInit() {
this.route.paramMap.subscribe(params => {
this.product = products[+params.get("productId")];
});
}
}

文件product-details.component.html

1
2
3
4
5
6
<h2>Product Details</h2>
<div *ngIf="product">
<h3>{ { product.name }}</h3>
<h4>{ { product.price | currency }}</h4>
<p>{ { product.description }}</p>
</div>

现在,当用户点击商品列表中的某个名字时,路由器就会导航到商品的不同网址。

用商品详情组件代替商品列表组件,并显示商品详情。

四、管理数据

服务是 Angular 应用的重要组成部分。

在 Angular 中,服务是一个类的实例,它可以借助 Angular 的依赖注入系统来让应用中的任何一个部件都能使用它。

服务可以让你在应用的各个部件之间共享数据。对于在线商店,购物车服务就是存放购物车的数据和方法的地方。

1、定义购物车服务

1
ng generate service cart

文件cart.service.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
import { Injectable } from "@angular/core";
import { HttpClient } from '@angular/common/http';

@Injectable({
providedIn: "root"
})
export class CartService {
//定义一个 items 属性来把当前商品的数组存储在购物车中
items = [];
constructor() {}
//把商品添加到购物车方法
addToCart(product) {
this.items.push(product);
}
//返回购物车商品方法
getItems() {
return this.items;
}
//清除购物车商品的方法
clearCart() {
this.items = [];
return this.items;
}
}

2、使用购物车服务

文件product-details.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
28
29
30
31
32
33
34
import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";

import { products } from "../products";

//导入购物车服务
import { CartService } from "../cart.service";

@Component({
selector: "app-product-details",
templateUrl: "./product-details.component.html",
styleUrls: ["./product-details.component.css"]
})
export class ProductDetailsComponent implements OnInit {
product;

//通过把购物车服务注入到这里的 constructor() 中来注入它
constructor(
private route: ActivatedRoute,
private cartService: CartService
) {}

//定义 addToCart() 方法,该方法会当前商品添加到购物车中
addToCart(product) {
window.alert('Your product has been added to the cart!');
this.cartService.addToCart(product);
}

ngOnInit() {
this.route.paramMap.subscribe(params => {
this.product = products[+params.get("productId")];
});
}
}

文件product-details.component.html

1
2
3
4
5
6
7
8
<h2>Product Details</h2>
<div *ngIf="product">
<h3>{ { product.name }}</h3>
<h4>{ { product.price | currency }}</h4>
<p>{ { product.description }}</p>
<!-- 添加一个标签为“Buy”的按钮,并把其 click() 事件绑定到 addToCart() 方法 -->
<button (click)="addToCart(product)">Buy</button>
</div>

3、创建购物车页面 

1
ng generate component cart

 文件cart.component.html

1
2
3
4
5
6
<h3>Cart</h3>

<div class="cart-item" *ngFor="let item of items">
<span>{ { item.name }}</span>
<span>{ { item.price | currency }}</span>
</div>

4、为购物车组件添加路由(URL 模式)

文件app.module.ts

1
2
3
4
5
6
7
8
9
10
11
@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: '', component: ProductListComponent },
{ path: 'products/:productId', component: ProductDetailsComponent },
//为组件 CartComponent 添加一个路由,其路由为 cart
{ path: 'cart', component: CartComponent },
])
],