本章基于以一个基本 Angular 应用快速上手的第二步 —— 添加导航。 在此开发阶段,本商店应用具有一个包含两个视图的商品名录:商品列表和商品详情。用户点击清单中的某个商品名称,就会在新视图中看到具有专门的 URL 或路由的详情页。
本页将指导你分三个步骤创建购物车:
HttpClient
从 .json
文件中检索配送数据来取得购物车中这些商品的运费。在 Angular 中, 服务是类的一个实例, 借助 Angular 的依赖注入体系,你可以在应用中的任意部分使用它。
现在, 用户可以浏览产品信息,而应用可以模拟分享产品,以及发出产品变更通知。
下一步是为用户提供一种把产品添加到购物车中的方法。 本章节将带领你添加一个 Buy 按钮并且建立一个购物车服务以保存购物车中的产品信息。
本节将引导你创建用于跟踪添加到购物车的产品的 CartService
。
cart
服务:ng generate service cart
Product
接口从 ./products.ts
导入到 cart.service.ts
文件中,在 CartService
类中,定义一个 items
属性来存储购物车中当前产品的数组。import { Product } from "./products";
export class CartService {
items: Product[] = [];
}
export class CartService {
items: Product[] = [];
addToCart(product: Product) {
this.items.push(product);
}
getItems() {
return this.items;
}
clearCart() {
this.items = [];
return this.items;
}
}
addToCart()
方法会将产品附加到 items
数组中。getItems()
方法会收集用户加到购物车中的商品,并返回每个商品及其数量。clearCart()
方法返回一个空数组。本节会教你使用 CartService
来把一个商品添加到购物车中。
product-details.component.ts
中导入购物车服务。import { Component, OnInit } from "@angular/core";
import { ActivatedRoute } from "@angular/router";
import { Product, products } from "../products";
import { CartService } from "../cart.service";
constructor()
中来注入它。export class ProductDetailsComponent implements OnInit {
constructor(
private route: ActivatedRoute,
private cartService: CartService
) { }
}
addToCart()
方法,该方法会当前商品添加到购物车中。export class ProductDetailsComponent implements OnInit {
addToCart(product: Product) {
this.cartService.addToCart(product);
window.alert("Your product has been added to the cart!");
}
}
addToCart()
方法做了如下事情:
CartService
addToCart()
方法去添加产品到购物车中。product-details.component.html
中,添加一个带有 Buy 标签的按钮,并且把其 click()
事件绑定到 addToCart()
方法上。 这段代码会为产品详情模板添加一个 Buy 按钮,并把当前产品添加到购物车中。<h2>Product Details</h2>
<div *ngIf="product">
<h3>{{ product.name }}</h3>
<h4>{{ product.price | currency }}</h4>
<p>{{ product.description }}</p>
<button (click)="addToCart(product)">Buy</button>
</div>
Buy
按钮如预期般出现了,并且单击某个产品的名称,以展示其详情。
为了让顾客看到他们的购物车,你可以用两步创建购物车视图:
要创建购物车视图,可遵循与创建 ProductDetailsComponent
相同的步骤,并且为这个新组件配置路由。
cart
的新组件:ng generate component cart
此命令将生成 cart.component.ts
文件及其关联的模板和样式文件。
import { Component } from "@angular/core";
@Component({
selector: "app-cart",
templateUrl: "./cart.component.html",
styleUrls: ["./cart.component.css"]
})
export class CartComponent {
constructor() { }
}
StackBlitz 还在组件中默认生成一个 ngOnInit()
。对于本教程,你可以忽略 CartComponent
的 ngOnInit()
。
CartComponent
已添加到 app.module.ts
中模块的 declarations
中。import { CartComponent } from "./cart/cart.component";
@NgModule({
declarations: [
AppComponent,
TopBarComponent,
ProductListComponent,
ProductAlertsComponent,
ProductDetailsComponent,
CartComponent,
],
app.module.ts
,为组件 CartComponent
添加一个路由,其路由为 cart
:@NgModule({
imports: [
BrowserModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: "", component: ProductListComponent },
{ path: "products/:productId", component: ProductDetailsComponent },
{ path: "cart", component: CartComponent },
])
],
/cart
。 在 top-bar.component.html
中添加一个指向 /cart
的 routerLink
指令。<a routerLink="/cart" class="button fancy-button">
<i class="material-icons">shopping_cart</i>Checkout
</a>
https://getting-started.stackblitz.io/cart
,其中的 getting-started.stackblitz.io 部分可能与你的 StackBlitz 项目不同。
本节将告诉你如何修改购物车组件以使用购物车服务来显示购物车中的商品。
cart.component.ts
中,从 cart.service.ts
文件中导入 CartService
。import { Component } from "@angular/core";
import { CartService } from "../cart.service";
CartService
,以便购物车组件可以使用它。export class CartComponent {
constructor(
private cartService: CartService
) { }
}
items
属性,以便把商品存放在购物车中。export class CartComponent {
items = this.cartService.getItems();
constructor(
private cartService: CartService
) { }
}
这段代码使用 CartService
的 getItems()
方法来设置条目。
*ngFor
的 <div>
来显示每个购物车商品的名字和价格。生成的 CartComponent
模板如下:
<h3>Cart</h3>
<div class="cart-item" *ngFor="let item of items">
<span>{{ item.name }}</span>
<span>{{ item.price | currency }}</span>
</div>
服务器通常采用流的形式返回数据。 流是很有用的,因为它们可以很容易地转换返回的数据,也可以修改你请求数据的方式。 Angular 的 HTTP 客户端( HttpClient
)是一种内置的方式,可以从外部 API 中获取数据,并以流的形式提供给你的应用。
本节会为你展示如何使用 HttpClient
从外部文件中检索运费。
在本指南的 StackBlitz 应用中,通过 assets/shipping.json
文件提供了一些预定义的配送数据。你可以利用这些数据为购物车中的商品添加运费。
[
{
"type": "Overnight",
"price": 25.99
},
{
"type": "2-Day",
"price": 9.99
},
{
"type": "Postal",
"price": 2.99
}
]
要使用 Angular 的 HTTP 客户端之前,你必须先配置你的应用来使用 HttpClientModule
。
Angular 的 HttpClientModule
中注册了在整个应用中使用 HttpClient
服务的单个实例所需的服务提供者。
app.module.ts
的顶部从 @angular/common/http
包中导入 HttpClientModule
以及其它导入项。 由于有很多其它导入项,因此这里的代码片段省略它们,以保持简洁。请确保现有的导入都还在原地。import { HttpClientModule } from "@angular/common/http";
HttpClientModule
添加到 AppModule
@NgModule()
的 imports
数组中,以便全局注册 Angular 的 HttpClient
。@NgModule({
imports: [
BrowserModule,
HttpClientModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: "", component: ProductListComponent },
{ path: "products/:productId", component: ProductDetailsComponent },
{ path: "cart", component: CartComponent },
])
],
declarations: [
AppComponent,
TopBarComponent,
ProductListComponent,
ProductAlertsComponent,
ProductDetailsComponent,
CartComponent,
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
下一步是注入 HttpClient
服务到你的服务中, 这样你的应用可以获取数据并且与外部API和资源互动。
@angular/common/http
包中导入 HttpClient
。import { Injectable } from "@angular/core";
import { HttpClient } from "@angular/common/http";
import { Product } from "./products";
HttpClient
注入到 CartService
的构造函数中:export class CartService {
items: Product[] = [];
constructor(
private http: HttpClient
) {}
}
要从 shapping.json
中得到商品数据, 你可以使用 HttpClient
get()
方法。
cart.service.ts
中 clearCart()
方法下面,定义一个新的 getShippingPrices()
方法,该方法会调用 HttpClient#get()
方法。export class CartService {
getShippingPrices() {
return this.http.get<{type: string, price: number}[]>("/assets/shipping.json");
}
}
现在你的应用已经可以检索配送数据了,你还要创建一个配送组件和相关的模板。
shipping
的组件:ng generate component shipping
右键单击 app
文件夹,选择 Angular Generator 和 Component 来生成一个名为 shipping
的新组件。
import { Component } from "@angular/core";
@Component({
selector: "app-shipping",
templateUrl: "./shipping.component.html",
styleUrls: ["./shipping.component.css"]
})
export class ShippingComponent {
constructor() { }
}
app.module.ts
中,添加一个配送路由。其 path
为 shipping
,其 component 为 ShippingComponent
。@NgModule({
imports: [
BrowserModule,
HttpClientModule,
ReactiveFormsModule,
RouterModule.forRoot([
{ path: "", component: ProductListComponent },
{ path: "products/:productId", component: ProductDetailsComponent },
{ path: "cart", component: CartComponent },
{ path: "shipping", component: ShippingComponent },
])
],
declarations: [
AppComponent,
TopBarComponent,
ProductListComponent,
ProductAlertsComponent,
ProductDetailsComponent,
CartComponent,
ShippingComponent
],
bootstrap: [
AppComponent
]
})
export class AppModule { }
新的配送组件尚未链接到任何其它组件,但你可以通过输入其路由指定的 URL 在预览窗格中看到它的模板。该 URL 具有以下模式:https://angular-ynqttp--4200.local.webcontainer.io/shipping
,其中的 gets-started.stackblitz.io 部分可能与你的 StackBlitz 项目不同。
这个章节将指导你修改 ShappingComponent
以通过HTTP从 shipping.json
文件中提取商品数据。
shipping.component.ts
中导入 CartService
。import { Component } from "@angular/core";
import { CartService } from "../cart.service";
ShippingComponent
的 constructor()
构造函数中:constructor(private cartService: CartService) { }
shippingCosts
属性,并从 CartService
中利用购物车服务的 getShippingPrices()
方法设置它。export class ShippingComponent {
shippingCosts = this.cartService.getShippingPrices();
}
async
管道修改配送组件的模板,以显示配送类型和价格:<h3>Shipping Prices</h3>
<div class="shipping-item" *ngFor="let shipping of shippingCosts | async">
<span>{{ shipping.type }}</span>
<span>{{ shipping.price | currency }}</span>
</div>
async
管道从数据流中返回最新值,并在所属组件的生命期内持续返回。当 Angular 销毁该组件时,async
管道会自动停止。
<h3>Cart</h3>
<p>
<a routerLink="/shipping">Shipping Prices</a>
</p>
<div class="cart-item" *ngFor="let item of items">
<span>{{ item.name }}</span>
<span>{{ item.price | currency }}</span>
</div>
点击此链接可以导航到运费页。
动态组件加载器译注:本页讲的是一个用于显示广告的范例,而部分广告拦截器插件,比如Chrome的AdGuard,可能会破坏其工作逻辑,...
依赖提供者通过配置提供者,你可以把服务提供给那些需要它们的应用部件。依赖提供者会使用DI令牌来配置注入器,注入器会用它来提...
里程碑3:英雄特征区本里程碑涵盖了以下内容:用模块把应用和路由组织为一些特性区。命令式的从一个组件导航到另一个通过路由传递...
构建动态表单许多表单(比如问卷)可能在格式和意图上都非常相似。为了更快更轻松地生成这种表单的不同版本,你可以根据描述业务...
部署多个语言环境如果myapp是包含项目可分发文件的目录,你通常会在语言环境目录中为不同的语言环境提供不同的版本,比如法...
前提条件对下列概念有基本的理解:Angular动画简介转场与触发器到目前为止,我们已经学过了单个HTML元素的简单动画。Angular还允...