Monday, February 25, 2019

Design an Angular 2+ shopping module PART 2

Now I'll need an user story to figure out what services and functionalities I need.

The user story...


  1. When I see an item, whatever is for sale, it's a Product. I can add it to my shopping cart.
  2. I can quick link to my shopping cart wherever in the APP.
  3. I can change the quantity of purchased product
  4. It'll require my contact information to complete the order.
  5. I have to choose a payment method to complete the order.
  6. I have to follow the instruction of payment method to complete payment.
  7. When payment confirmed, I need to be noticed that the order is confirmed paid.
  8. I can review the order detail anytime.
  9. I can check a list of all my orders.

The corresponding UI widgets, services and functionalities...

  1. A cart service:
    i. Hold the added purchases in cart.
    ii. Functions of add/remove/clear item(s) to/from cart.
  2. A cart-link-button which can be put anywhere in APP, header or footer or sidebar tools. Clicking on that button will show cart page.
  3. An inline number input for every purchase's quantity field.
    When this input changed, total price reflects the change immediately.
    When user add, remove, or clear purchases, re-calculate the total price, too.
    In addition to above cases, any change to the purchases is significant and need to be synced right away in the cart, so we'll need an Observable in cart service to broadcast the change.
  4. A contact form to fill in the contact information, which requires name, and at least one of  phone or email.
  5. A payment-method selector which is a single-select radio group, shows each payment-method's name and description.
  6. The 3rd-party payment service should handle this part well.
  7. The backend should be responsible for notifications.
  8. An  order service which can...
    i. fetch any order by id.
    ii. create order by composing purchases, contact, and payment data.
    iii. Consider that information should be consistent through entire transaction, order data is immutable once created by user.
    And an order-view component to show its detail.
  9. An order-list component which list orders of on specified user.
Too much code... Here is only the part of cart service.

import { Injectable } from '@angular/core';
import { Product } from '../purchase/product';
import { Purchase } from '../purchase/purchase';
import { BehaviorSubject, Observable, of, isObservable, throwError } from 'rxjs';
import { OrderService } from '../order/order.service';
import { Order } from '../order/order';
import { first, switchMap, take } from 'rxjs/operators';

@Injectable({
providedIn: 'root'
})
export class CartService {
purchases: Purchase[];
contact: any;
purchasesChange: BehaviorSubject<Purchase[]>;
paymentMethodId: string;
currentUserId$: BehaviorSubject<string>;

constructor(
protected orderService: OrderService
) {
this.purchases = [];
this.purchasesChange = new BehaviorSubject<Purchase[]>(this.purchases);
this.currentUserId$ = new BehaviorSubject<string>(null);
}

set currentUserId(value: string | Observable<string>) {
if (value) {
if (typeof value === 'string') {
this.currentUserId$.next(value);
} else if (isObservable<string>(value)) {
value.subscribe(this.currentUserId$);
}
}
}

add(product: Product, quantity: number = 1) {
const newPurchase = new Purchase(product, quantity);
this.purchases.push(newPurchase);
this.purchasesChange.next(this.purchases);
}

remove(index: number) {
const purchase = this.purchases[index];
if (purchase && confirm(`確定要移除購買項目: ${purchase.product.name} ?`)) {
this.purchases.splice(index, 1);
this.purchasesChange.next(this.purchases);
}
}

clear() {
this.purchases = [];
this.contact = {};
this.purchasesChange.next(this.purchases);
}

checkout(): Observable<Order> {
return this.currentUserId$.pipe(
switchMap(ownerId => {
if (!ownerId) {
alert('請先登入再結帳');
return throwError(new Error(`User not login`));
} else {
return this.orderService.create(this.purchases, this.contact, this.paymentMethodId, ownerId)
}
}),
take(1)
);
}
}


No comments:

Post a Comment