In the previous practice, we had successfully created an Angular2 app with ngrx/store to maintain and manage the shopping cart state in our application.
Let’s improve the application by storing the individual product’s information in the state, and therefore we can show what a user are going to buy from the shopping cart.
Add a ShopItem[]
array into the ShopCart
class.
import { IShopCart } from '../interface/IShopCart';
import { ShopItem } from '../class/ShopItem';
export class ShopCart implements IShopCart {
items: ShopItem[];
cnt: number;
sum: number;
constructor() {
this.items = [];
this.cnt = 0;
this.sum = 0;
}
}
Also we should keep the id
and title
of the item we put into the shopping cart.
export class ShopItem {
id: string; //Product ID
title: string; //Product name
count: number; //Counter
price: number; //Cash summary
}
We should implement the following logic in actions.
import { BehaviorSubject } from 'rxjs/BehaviorSubject';
import { Action, ActionReducer } from '@ngrx/store';
import { IShopCart } from '../interface/IShopCart';
import { ShopCart } from '../class/ShopCart';
import { ShopItem } from '../class/ShopItem';
export const PUSH = 'PUSH';
export const PULL = 'PULL';
export const CLEAR = 'CLEAR';
export const shopcartReducer: ActionReducer<IShopCart> = (state: ShopCart = new ShopCart(), action: Action) => {
switch (action.type) {
case PUSH:
return pushToCart(state, action.payload);
case PULL:
return pullFromCart(state, action.payload);
case CLEAR:
state.cnt = 0;
state.sum = 0;
state.items = [];
return state;
default:
return state;
}
}
function pushToCart(shopcart: ShopCart, payload: ShopItem) {
shopcart.cnt += 1;
shopcart.sum += payload.price * 1;
updateItems(shopcart, payload);
return shopcart;
}
function pullFromCart(shopcart: ShopCart, payload: ShopItem) {
shopcart.cnt -= 1;
shopcart.sum -= payload.price * 1;
updateItems(shopcart, payload);
return shopcart;
}
function updateItems(shopcart: ShopCart, payload: ShopItem) {
let targetItem = shopcart.items.find(item => item.id === payload.id);
if (targetItem) { //Exist
if (payload.count <= 0) {
var index = shopcart.items.indexOf(targetItem);
shopcart.items.splice(index, 1);
}
else {
targetItem.count = payload.count;
}
}
else { //First time adding to shopping cart
shopcart.items.push(payload);
}
}
Okay, it's done. We have successfully made the state supports maitaining the individual product information.
Let’s create a new component for showing the items in the shopping cart.
import { Component, Pipe, PipeTransform } from '@angular/core';
import { Router } from '@angular/router';
import { Observable } from 'rxjs/Observable';
import { Store } from '@ngrx/store';
import { PUSH, PULL, CLEAR } from '../../../service/shopcart.action';
import { IShopCart } from '../../../interface/IShopCart';
import { ShopCart } from '../../../class/ShopCart';
import { ShopItem } from '../../../class/ShopItem';
@Component({
selector: 'shop-cart',
providers: [],
template: `
<div><button class="btn btn-default" (click)="goToProducts()"><i class="fa fa-ra"></i> Back </button></div>
<div>
<table class="table table-inverse">
<thead>
<tr>
<th>Product</th>
<th>Number</th>
<th>Price/per</th>
<th>Total Price</th>
</tr>
</thead>
<tbody>
<tr *ngFor="let item of (shopcart | async)?.items">
<td>{{item.title}}</td>
<td>{{item.count}}</td>
<td>{{item.price}}</td>
<td>{{item.count * item.price}}</td>
</tr>
</tbody>
</table>
</div>
`
})
export class ShopcartComponent implements OnInit {
private shopcart: Observable<IShopCart>;
constructor(
private router: Router,
private store: Store<IShopCart>
) {
//Get the reducer
this.shopcart = store.select("shopcart");
}
}
Last step, we will use the current state to get the items in the shopping cart, and then update the display value in the view.
Take ProductBooksComponent
for example,
ngOnInit() {
this.initBooks();
this.initToastrOptions();
}
//Initialize books
private initBooks() {
this.productService.getBooks().then(data => {
this.books = data;
//Use shopping cart to update data
this.shopcart.subscribe(cart => {
this.books.forEach(item => {
let storeItem = cart.items.find(x => x.id === item.Id);
if (!storeItem) {
this.itemNumbers[item.Id] = 0;
}
else {
this.itemNumbers[item.Id] = storeItem.count;
}
});
})
})
}
Everything is done for our shopping cart, all we have to do is send the state's information to backend after user clicks a save button or whatever.
(I will skip this practice … :)