填写这份《一分钟调查》,帮我们(开发组)做得更好!去填写Home

管理数据

Managing data

本章基于以一个基本 Angular 应用快速上手的第二步 —— 添加导航。 在此开发阶段,本商店应用具有一个包含两个视图的商品名录:商品列表和商品详情。用户点击清单中的某个商品名称,就会在新视图中看到具有专门的 URL 或路由的详情页。

This guide builds on the second step of the Getting started with a basic Angular application tutorial, Adding navigation. At this stage of development, the store application has a product catalog with two views: a product list and product details. Users can click on a product name from the list to see details in a new view, with a distinct URL, or route.

本页将指导你分三个步骤创建购物车:

This step of the tutorial guides you through creating a shopping cart in the following phases:

  • 修改商品详情视图,让它包含一个 “Buy” 按钮,它会把当前商品添加到由 "购物车服务" 管理的商品列表中。

    Update the product details view to include a Buy button, which adds the current product to a list of products that a cart service manages.

  • 添加一个购物车组件,它会显示购物车中的商品。

    Add a cart component, which displays the items in the cart.

  • 添加一个配送组件,它会使用 Angular 的 HttpClient.json 文件中检索配送数据来取得购物车中这些商品的运费。

    Add a shipping component, which retrieves shipping prices for the items in the cart by using Angular's HttpClient to retrieve shipping data from a .json file.

创建购物车服务

Create the shopping cart service

在 Angular 中, 服务是类的一个实例, 借助 Angular 的依赖注入体系,你可以在应用中的任意部分使用它。

In Angular, a service is an instance of a class that you can make available to any part of your application using Angular's dependency injection system.

现在, 用户可以浏览产品信息,而应用可以模拟分享产品,以及发出产品变更通知。

Currently, users can view product information, and the application can simulate sharing and notifications about product changes.

下一步是为用户提供一种把产品添加到购物车中的方法。 本章节将带领你添加一个 Buy 按钮并且建立一个购物车服务以保存购物车中的产品信息。

The next step is to build a way for users to add products to a cart. This section walks you through adding a Buy button and setting up a cart service to store information about products in the cart.

定义购物车服务

Define a cart service

  1. 要想生成购物车服务,请右键单击 app 文件夹,选择 Angular Generator,并选择 Service。把这个新服务命名为 cart

    To generate a cart service, right click on the app folder, choose Angular Generator, and choose Service. Name the new service cart.

    src/app/cart.service.ts
          
          import { Injectable } from '@angular/core';
    
    @Injectable({
      providedIn: 'root'
    })
    export class CartService {
    
      constructor() {}
    
    }
        
  2. CartService 类中,定义一个 items 属性来把当前商品的数组存储在购物车中。

    In the CartService class, define an items property to store the array of the current products in the cart.

    src/app/cart.service.ts
          
          export class CartService {
      items: Product[] = [];
    }
        
  3. 定义把商品添加到购物车、返回购物车商品以及清除购物车商品的方法:

    Define methods to add items to the cart, return cart items, and clear the cart items.

    src/app/cart.service.ts
          
          export class CartService {
      items: Product[] = [];
    
      addToCart(product: Product) {
        this.items.push(product);
      }
    
      getItems() {
        return this.items;
      }
    
      clearCart() {
        this.items = [];
        return this.items;
      }
    }
        
  • addToCart() 方法会将产品附加到 items 数组中。

    The addToCart() method appends a product to an array of items.

    • getItems() 方法会收集用户加到购物车中的商品,并返回每个商品及其数量。

      The getItems() method collects the items users add to the cart and returns each item with its associated quantity.

    • clearCart() 方法返回一个空数组。

      The clearCart() method returns an empty array of items, which empties the cart.

使用购物车服务

Use the cart service

本节会教你使用 CartService 来把一个商品添加到购物车中。

This section walks you through using the CartService to add a product to the cart.

  1. product-details.component.ts 中导入购物车服务。

    In product-details.component.ts, import the cart service.

    src/app/product-details/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';
        
    1. 通过把购物车服务注入到这里的 constructor() 中来注入它。

      Inject the cart service by adding it to the constructor().

      src/app/product-details/product-details.component.ts
            
            export class ProductDetailsComponent implements OnInit {
        constructor(
          private route: ActivatedRoute,
          private cartService: CartService
        ) { }
      }
          
  2. 定义 addToCart() 方法,该方法会当前商品添加到购物车中。

    Define the addToCart() method, which adds the current product to the cart.

    src/app/product-details/product-details.component.ts
          
          export class ProductDetailsComponent implements OnInit {
      addToCart(product: Product) {
        this.cartService.addToCart(product);
        window.alert('Your product has been added to the cart!');
      }
    }
        

    addToCart() 方法做了如下事情:

    The addToCart() method does the following:

    • 以当前'product'作为参数。

      Takes the current product as an argument.

    • 使用 CartService addToCart() 方法去添加产品到购物车中。

      Uses the CartService addToCart() method to add the product to the cart.

    • 显示一条你已经添加了一个产品到购物车到消息。

      Displays a message that you've added a product to the cart.

  3. product-details.component.html 中,添加一个带有 Buy 标签的按钮,并且把其 click() 事件绑定到 addToCart() 方法上。 这段代码会为产品详情模板添加一个 Buy 按钮,并把当前产品添加到购物车中。

    In product-details.component.html, add a button with the label Buy, and bind the click() event to the addToCart() method. This code updates the product details template with a Buy button that adds the current product to the cart.

    src/app/product-details/product-details.component.html
          
          <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>
        
  1. 刷新应用,以验证新的 Buy 按钮如预期般出现了,并且单击某个产品的名称,以展示其详情。

    Verify that the new Buy button appears as expected by refreshing the application and clicking on a product's name to display its details.

  2. 点击“Buy”按钮来把该商品添加到购物车中存储的商品列表中,并显示一条确认消息。

    Click the Buy button to add the product to the stored list of items in the cart and display a confirmation message.

创建购物车视图

Create the cart view

为了让顾客看到他们的购物车,你可以用两步创建购物车视图:

For customers to see their cart, you can create the cart view in two steps:

  1. 创建一个购物车组件并配置指向这个新组件的路由。

    Create a cart component and configure routing to the new component.

  2. 显示购物车商品

    Display the cart items.

设置该组件

Set up the cart component

要创建购物车视图,可遵循与创建 ProductDetailsComponent 相同的步骤,并且为这个新组件配置路由。

To create the cart view, follow the same steps you did to create the ProductDetailsComponent and configure routing for the new component.

  1. 右键单击 app 文件夹,选择 Angular GeneratorComponent 以生成一个名为 cart 的购物车组件。

    Generate a cart component named cart by right-clicking the app folder, choosing Angular Generator, and Component.

    src/app/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()。不过在本教程中,你可以忽略 CartComponentngOnInit()

    StackBlitz also generates an ngOnInit() by default in components. You can ignore the CartComponent ngOnInit() for this tutorial.

  2. 打开 app.module.ts,为组件 CartComponent 添加一个路由,其路由为 cart

    Open app.module.ts and add a route for the component CartComponent, with a path of cart.

    src/app/app.module.ts
          
          @NgModule({
      imports: [
        BrowserModule,
        ReactiveFormsModule,
        RouterModule.forRoot([
          { path: '', component: ProductListComponent },
          { path: 'products/:productId', component: ProductDetailsComponent },
          { path: 'cart', component: CartComponent },
        ])
      ],
        
  3. 修改 "Checkout" 按钮,以便让它路由到 /cart。 在 top-bar.component.html 中添加一个指向 /cartrouterLink 指令。

    Update the Checkout button so that it routes to the /cart URL. In top-bar.component.html, add a routerLink directive pointing to /cart.

    src/app/top-bar/top-bar.component.html
          
          <a routerLink="/cart" class="button fancy-button">
      <i class="material-icons">shopping_cart</i>Checkout
    </a>
        
  4. 要查看新的购物车组件,请点击“Checkout”按钮。你会看到默认文本“cart works!”,该 URL 的格式为 https://getting-started.stackblitz.io/cart,其中的 getting-started.stackblitz.io 部分可能与你的 StackBlitz 项目不同。

    Verify the new CartComponent works as expected by clicking the Checkout button. You can see the "cart works!" default text, and the URL has the pattern https://getting-started.stackblitz.io/cart, where getting-started.stackblitz.io may be different for your StackBlitz project.

显示购物车商品

Display the cart items

本节将告诉你如何修改购物车组件以使用购物车服务来显示购物车中的商品。

This section shows you how to use the cart service to display the products in the cart.

  1. cart.component.ts 中,从 cart.service.ts 文件中导入 CartService

    In cart.component.ts, import the CartService from the cart.service.ts file.

    src/app/cart/cart.component.ts
          
          import { Component } from '@angular/core';
    import { CartService } from '../cart.service';
        
    1. 注入 CartService,以便购物车组件可以使用它。

      Inject the CartService so that the CartComponent can use it by adding it to the constructor().

      src/app/cart/cart.component.ts
            
            export class CartComponent {
      
        constructor(
          private cartService: CartService
        ) { }
      }
          
  2. 定义 items 属性,以便把商品存放在购物车中。

    Define the items property to store the products in the cart.

    src/app/cart/cart.component.ts
          
          export class CartComponent {
      items = this.cartService.getItems();
    
      constructor(
        private cartService: CartService
      ) { }
    }
        

    这段代码使用 CartServicegetItems() 方法来设置条目。你以前在创建 cart.service.ts时定义过此方法。

    This code sets the items using the CartService getItems() method. You defined this method when you created cart.service.ts.

  3. 修改模板,加上标题,用带有 *ngFor<div> 来显示每个购物车商品的名字和价格。

    Update the cart template with a header, and use a <div> with an *ngFor to display each of the cart items with its name and price.

    生成的 CartComponent 模板如下:

    The resulting CartComponent template is as follows.

    src/app/cart/cart.component.html
          
          <h3>Cart</h3>
    
    <div class="cart-item" *ngFor="let item of items">
      <span>{{ item.name }}</span>
      <span>{{ item.price | currency }}</span>
    </div>
        
  4. 验证你的购物车如预期般工作:

    Verify that your cart works as expected:

    • 点击 My Store

      Click My Store

    • 单击商品名称以显示其详细信息。

      Click on a product name to display its details.

    • 点击Buy 将商品添加到购物车。

      Click Buy to add the product to the cart.

    • 点击Checkout查看购物车。

      Click Checkout to see the cart.

要了解关于服务的更多信息,请参阅“服务和依赖注入简介”

For more information about services, see Introduction to Services and Dependency Injection.

检索运费价格

Retrieve shipping prices

服务器通常采用流的形式返回数据。 流是很有用的,因为它们可以很容易地转换返回的数据,也可以修改你请求数据的方式。 Angular 的 HTTP 客户端( HttpClient )是一种内置的方式,可以从外部 API 中获取数据,并以流的形式提供给你的应用。

Servers often return data in the form of a stream. Streams are useful because they make it easy to transform the returned data and make modifications to the way you request that data. Angular HttpClient is a built-in way to fetch data from external APIs and provide them to your application as a stream.

本节会为你展示如何使用 HttpClient 从外部文件中检索运费。

This section shows you how to use HttpClient to retrieve shipping prices from an external file.

在本指南的 StackBlitz 应用中,通过 assets/shipping.json 文件提供了一些预定义的配送数据。你可以利用这些数据为购物车中的商品添加运费。

The application that StackBlitz generates for this guide comes with predefined shipping data in assets/shipping.json. Use this data to add shipping prices for items in the cart.

src/assets/shipping.json
      
      [
  {
    "type": "Overnight",
    "price": 25.99
  },
  {
    "type": "2-Day",
    "price": 9.99
  },
  {
    "type": "Postal",
    "price": 2.99
  }
]
    

配置 AppModule 以使用 HttpClient

Configure AppModule to use HttpClient

要使用 Angular 的 HTTP 客户端之前,你必须先配置你的应用来使用 HttpClientModule

To use Angular's HttpClient, you must configure your application to use HttpClientModule.

Angular 的 HttpClientModule 中注册了在整个应用中使用 HttpClient 服务的单个实例所需的服务提供者。

Angular's HttpClientModule registers the providers your application needs to use the HttpClient service throughout your application.

  1. app.module.ts 的顶部从 @angular/common/http 包中导入 HttpClientModule 以及其它导入项。 由于有很多其它导入项,因此这里的代码片段省略它们,以保持简洁。请确保现有的导入都还在原地。

    In app.module.ts, import HttpClientModule from the @angular/common/http package at the top of the file with the other imports. As there are a number of other imports, this code snippet omits them for brevity. Be sure to leave the existing imports in place.

    src/app/app.module.ts
          
          import { HttpClientModule } from '@angular/common/http';
        
  2. HttpClientModule 添加到 AppModule @NgModule()imports 数组中,以便全局注册 Angular 的 HttpClient

    To register Angular's HttpClient providers globally, add HttpClientModule to the AppModule @NgModule() imports array.

    src/app/app.module.ts
          
          @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 { }
        

配置 CartService 以使用 HttpClient

Configure CartService to use HttpClient

下一步是注入 HttpClient 服务到你的服务中, 这样你的应用可以获取数据并且与外部API和资源互动。

The next step is to inject the HttpClient service into your service so your application can fetch data and interact with external APIs and resources.

  1. @angular/common/http 包中导入 HttpClient

    In cart.service.ts, import HttpClient from the @angular/common/http package.

    src/app/cart.service.ts
          
          import { Injectable } from '@angular/core';
    import { HttpClient } from '@angular/common/http';
    import { Product } from './products';
        
  2. HttpClient 注入到 CartService 的构造函数中:

    Inject HttpClient into the CartService constructor().

    src/app/cart.service.ts
          
          export class CartService {
      items: Product[] = [];
    
      constructor(
        private http: HttpClient
      ) {}
    }
        

配置 CartService 以得到商品价格

Configure CartService to get shipping prices

要从 shapping.json 中得到商品数据, 你可以使用 HttpClient get() 方法。

To get shipping data, from shipping.json, You can use the HttpClient get() method.

  1. cart.service.tsclearCart() 方法下面,定义一个新的 getShippingPrices() 方法,该方法会调用 HttpClient#get() 方法。

    In cart.service.ts, below the clearCart() method, define a new getShippingPrices() method that uses the HttpClient get() method.

    src/app/cart.service.ts
          
          export class CartService {
      getShippingPrices() {
        return this.http.get<{type: string, price: number}[]>('/assets/shipping.json');
      }
    }
        

要了解关于 Angular HttpClient 的更多信息,请参阅客户端-服务器集成指南。

For more information about Angular's HttpClient, see the Client-Server Interaction guide.

创建配送组件

Create a shipping component

现在你的应用已经可以检索配送数据了,你还要创建一个配送组件和相关的模板。

Now that you've configured your application to retrieve shipping data, you can create a place to render that data.

  1. 右键单击 app 文件夹,选择 Angular GeneratorComponent 来生成一个名为 shipping 的新组件。

    Generate a new component named shipping by right-clicking the app folder, choosing Angular Generator, and selecting Component.

    src/app/shipping/shipping.component.ts
          
          import { Component } from '@angular/core';
    
    @Component({
      selector: 'app-shipping',
      templateUrl: './shipping.component.html',
      styleUrls: ['./shipping.component.css']
    })
    export class ShippingComponent {
    
      constructor() { }
    
    }
        
  2. app.module.ts 中,添加一个配送路由。其 pathshipping,其 component 为 ShippingComponent

    In app.module.ts, add a route for shipping. Specify a path of shipping and a component of ShippingComponent.

    src/app/app.module.ts
          
          @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://getting-started.stackblitz.io/shipping,其中的 gets-started.stackblitz.io 部分可能与你的 StackBlitz 项目不同。

    There's no link to the new shipping component yet, but you can see its template in the preview pane by entering the URL its route specifies. The URL has the pattern: https://getting-started.stackblitz.io/shipping where the getting-started.stackblitz.io part may be different for your StackBlitz project.

配置 ShippingComponent 以使用 CartService

Configuring the ShippingComponent to use CartService

这个章节将指导你修改 ShappingComponent 以通过HTTP从 shipping.json 文件中提取商品数据。

This section guides you through modifying the ShippingComponent to retrieve shipping data via HTTP from the shipping.json file.

  1. shipping.component.ts 中导入 CartService

    In shipping.component.ts, import CartService.

    src/app/shipping/shipping.component.ts
          
          import { Component } from '@angular/core';
    
    import { CartService } from '../cart.service';
        

  2. 把购物车服务注入到 ShippingComponentconstructor() 构造函数中:

    Inject the cart service in the ShippingComponent constructor().

    src/app/shipping/shipping.component.ts
          
          constructor(private cartService: CartService) {
    }
        

    1. 定义一个 shippingCosts 属性,并从 CartService 中利用购物车服务的 getShippingPrices() 方法设置它。

      Define a shippingCosts property that sets the shippingCosts property using the getShippingPrices() method from the CartService.

      src/app/shipping/shipping.component.ts
            
            export class ShippingComponent {
        shippingCosts = this.cartService.getShippingPrices();
      }
          
  3. 利用 async 管道修改配送组件的模板,以显示配送类型和价格:

    Update the ShippingComponent template to display the shipping types and prices using the async pipe.

    src/app/shipping/shipping.component.html
          
          <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 管道会自动停止。关于 async 管道的详细信息,请参阅 AsyncPipe API 文档

    The async pipe returns the latest value from a stream of data and continues to do so for the life of a given component. When Angular destroys that component, the async pipe automatically stops. For detailed information about the async pipe , see the AsyncPipe API documentation.

  4. 在购物车视图中添加一个到配送视图的链接:

    Add a link from the CartComponent view to the ShippingComponent view.

    src/app/cart/cart.component.html
          
          <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>
        
  5. 点击 Checkout 按钮,查看更新后的购物车。注意,修改本应用会导致预览窗格刷新,这会清空购物车。

    Click the Checkout button to see the updated cart. Remember that changing the application causes the preview to refresh, which empties the cart.

    点击此链接可以导航到运费页。

    Click on the link to navigate to the shipping prices.

下一步

What's next

现在你有了一个带有商品名录和购物车的商店应用了,而且你还可以查询运费。

You now have a store application with a product catalog, a shopping cart, and you can look up shipping prices.

要继续探索 Angular,你可以:

To continue exploring Angular:

  • 继续阅读表单与用户输入部分,添加购物车视图和结账视图,以完成本应用。

    Continue to Forms for User Input to finish the application by adding the shopping cart view and a checkout form.

  • 跳到部署部分来转为本地开发,或者把你的应用部署到 Firebase 或你自己的服务器上。

    Skip ahead to Deployment to move to local development, or deploy your application to Firebase or your own server.