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:
HttpClient
to retrieve shipping data from a .json
fileIn 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.
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.
This section walks you through creating the CartService
that tracks products added to shopping cart.
In the terminal generate a new cart
service by running the following command:
ng generate service cart
Import the Product
interface from ./products.ts
into the cart.service.ts
file, and in the CartService
class, define an items
property to store the array of the current products in the cart.
import { Product } from './products'; /* . . . */ export class CartService { items: Product[] = []; /* . . . */ }
Define methods to add items to the cart, return cart items, and clear the cart items.
export class CartService { items: Product[] = []; /* . . . */ addToCart(product: Product) { this.items.push(product); } getItems() { return this.items; } clearCart() { this.items = []; return this.items; } /* . . . */ }
addToCart()
method appends a product to an array of items
getItems()
method collects the items users add to the cart and returns each item with its associated quantityclearCart()
method returns an empty array of items, which empties the cartThis section walks you through using the CartService
to add a product to the cart.
In product-details.component.ts
, import the cart service.
import { Component, OnInit } from '@angular/core'; import { ActivatedRoute } from '@angular/router'; import { Product, products } from '../products'; import { CartService } from '../cart.service';
Inject the cart service by adding it to the constructor()
.
export class ProductDetailsComponent implements OnInit { constructor( private route: ActivatedRoute, private cartService: CartService ) { } }
Define the addToCart()
method, which adds the current product to the cart.
export class ProductDetailsComponent implements OnInit { addToCart(product: Product) { this.cartService.addToCart(product); window.alert('Your product has been added to the cart!'); } }
The addToCart()
method does the following:
product
as an argumentCartService
addToCart()
method to add the product to the cartIn 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.
<h2>Product Details</h2> <div *ngIf="product"> <h3>{{ product.name }}</h3> <h4>{{ product.price | currency }}</h4> <p>{{ product.description }}</p> <button type="button" (click)="addToCart(product)">Buy</button> </div>
Verify that the new Buy button appears as expected by refreshing the application and clicking on a product's name to display its details.
Click the Buy button to add the product to the stored list of items in the cart and display a confirmation message.
For customers to see their cart, you can create the cart view in two steps:
To create the cart view, follow the same steps you did to create the ProductDetailsComponent
and configure routing for the new component.
Generate a new component named cart
in the terminal by running the following command:
ng generate component cart
This command will generate the cart.component.ts
file and its associated template and styles files.
import { Component } from '@angular/core'; @Component({ selector: 'app-cart', templateUrl: './cart.component.html', styleUrls: ['./cart.component.css'] }) export class CartComponent { }
StackBlitz also generates an ngOnInit()
by default in components. You can ignore the CartComponent
ngOnInit()
for this tutorial.
Notice that the newly created CartComponent
is added to the module's declarations
in app.module.ts
.
import { CartComponent } from './cart/cart.component'; @NgModule({ declarations: [ AppComponent, TopBarComponent, ProductListComponent, ProductAlertsComponent, ProductDetailsComponent, CartComponent, ],
Still in app.module.ts
, add a route for the component CartComponent
, with a path
of cart
.
@NgModule({ imports: [ BrowserModule, ReactiveFormsModule, RouterModule.forRoot([ { path: '', component: ProductListComponent }, { path: 'products/:productId', component: ProductDetailsComponent }, { path: 'cart', component: CartComponent }, ]) ],
Update the Checkout button so that it routes to the /cart
URL. In top-bar.component.html
, add a routerLink
directive pointing to /cart
.
<a routerLink="/cart" class="button fancy-button"> <i class="material-icons">shopping_cart</i>Checkout </a>
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.
This section shows you how to use the cart service to display the products in the cart.
In cart.component.ts
, import the CartService
from the cart.service.ts
file.
import { Component } from '@angular/core'; import { CartService } from '../cart.service';
Inject the CartService
so that the CartComponent
can use it by adding it to the constructor()
.
export class CartComponent { constructor( private cartService: CartService ) { } }
Define the items
property to store the products in the cart.
export class CartComponent { items = this.cartService.getItems(); constructor( private cartService: CartService ) { } }
This code sets the items using the CartService
getItems()
method. You defined this method when you created cart.service.ts
.
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. The resulting CartComponent
template is as follows.
<h3>Cart</h3> <div class="cart-item" *ngFor="let item of items"> <span>{{ item.name }}</span> <span>{{ item.price | currency }}</span> </div>
Verify that your cart works as expected:
For more information about services, see Introduction to Services and Dependency Injection.
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.
This section shows you how to use HttpClient
to retrieve shipping prices from an external file.
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.
[ { "type": "Overnight", "price": 25.99 }, { "type": "2-Day", "price": 9.99 }, { "type": "Postal", "price": 2.99 } ]
AppModule
to use HttpClient
To use Angular's HttpClient
, you must configure your application to use HttpClientModule
.
Angular's HttpClientModule
registers the providers your application needs to use the HttpClient
service throughout your application.
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.
import { HttpClientModule } from '@angular/common/http';
To register Angular's HttpClient
providers globally, add HttpClientModule
to the AppModule
@NgModule()
imports
array.
@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
to use HttpClient
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.
In cart.service.ts
, import HttpClient
from the @angular/common/http
package.
import { Injectable } from '@angular/core'; import { HttpClient } from '@angular/common/http'; import { Product } from './products';
Inject HttpClient
into the CartService
constructor()
.
export class CartService { items: Product[] = []; constructor( private http: HttpClient ) {} /* . . . */ }
CartService
to get shipping pricesTo get shipping data, from shipping.json
, You can use the HttpClient
get()
method.
In cart.service.ts
, below the clearCart()
method, define a new getShippingPrices()
method that uses the HttpClient
get()
method.
export class CartService { /* . . . */ getShippingPrices() { return this.http.get<{type: string, price: number}[]>('/assets/shipping.json'); } }
For more information about Angular's HttpClient
, see the Client-Server Interaction guide.
Now that you've configured your application to retrieve shipping data, you can create a place to render that data.
Generate a cart component named shipping
in the terminal by running the following command:
ng generate component shipping
This command will generate the shipping.component.ts
file and it associated template and styles files.
import { Component } from '@angular/core'; @Component({ selector: 'app-shipping', templateUrl: './shipping.component.html', styleUrls: ['./shipping.component.css'] }) export class ShippingComponent { }
In app.module.ts
, add a route for shipping. Specify a path
of shipping
and a component of 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 { }
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://angular-ynqttp--4200.local.webcontainer.io/shipping
where the angular-ynqttp--4200.local.webcontainer.io
part may be different for your StackBlitz project.
ShippingComponent
to use CartService
This section guides you through modifying the ShippingComponent
to retrieve shipping data via HTTP from the shipping.json
file.
In shipping.component.ts
, import CartService
.
import { Component, OnInit } from '@angular/core'; import { Observable } from 'rxjs'; import { CartService } from '../cart.service';
Inject the cart service in the ShippingComponent
constructor()
.
constructor(private cartService: CartService) { }
Define a shippingCosts
property that sets the shippingCosts
property using the getShippingPrices()
method from the CartService
. Initialize the shippingCosts
property inside ngOnInit()
method.
export class ShippingComponent implements OnInit { shippingCosts!: Observable<{ type: string, price: number }[]>; ngOnInit(): void { this.shippingCosts = this.cartService.getShippingPrices(); } }
Update the ShippingComponent
template to display the shipping types and prices using the async
pipe.
<h3>Shipping Prices</h3> <div class="shipping-item" *ngFor="let shipping of shippingCosts | async"> <span>{{ shipping.type }}</span> <span>{{ shipping.price | currency }}</span> </div>
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.
Add a link from the CartComponent
view to the ShippingComponent
view.
<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>
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.
You now have a store application with a product catalog, a shopping cart, and you can look up shipping prices.
To continue exploring Angular:
© 2010–2022 Google, Inc.
Licensed under the Creative Commons Attribution License 4.0.
https://angular.io/start/start-data