========== Angular ========= -> Angular is a client side framework developed by Google company -> Angular framework is developed using TypeScript -> Angular is mainley used for Single Page Applications Development (SPA) -> Angular supports multiple devices ( Mobiles & Desktops ) -> Angular supports multiple browsers -> Angular is free and open source framework -> Angular JS and Angular both are not same -> From Angular 2 version onwards it is called as Angular Framework ======================== Angular Building Blocks ======================== 1) Components 2) Metadata 3) Template 4) Data Binding 5) Module 6) Services 7) Dependency Injection 8) Directives 9) Pipes 10) Routers ================================ Step-1 : Angular Project Setup ================================ 1) Download and install VsCode IDE 2) Setup Angular Environment 3) Create Angular Application $ ng new ashokit_ecomm_frontend 4) Run Angular Application and Check $ cd ashokit_ecomm_frontend $ ng serve --open 5) Remove everything from "app.component.html" file and add your message Note: It should reflect in web page 6) Create AppConstants to declare backend api urls export class AppConstants { static PRODUCTS_ENDPOINT = "http://localhost:8080/products"; static CATEGORY_ENDPOINT = "http://localhost:8080/categories"; static PRODUCTS_SEARCH_ENDPOINT = "http://localhost:8080/productsByName"; } ================================================================= Step-2 : Retrieve Products From Backend and Display In Frontend ================================================================= @@@@ (1) Create Product class to bind backend-app response in frontend-app $ ng generate dto/product export class Product { constructor( public id: number, public name: string, public description: string, public title: string, public unitPrice: number, public imageUrl: string, public active: boolean, public unitsInStock: number, public dateCreated: Date, public lastUpdate: Date ) { } } @@@@ (2) Create AppConstants class under 'src' to configure backend-apis urls. export class AppConstants{ static PRODUCTS_ENDPOINT = "http://localhost:8080/products/1"; } @@@@ (3) Create Service class to make HTTP call to backend app $ ng generate services/product export class ProductService { constructor(private http: HttpClient) { } getAllProducts(): Observable { return this.http.get(`${AppConstants.PRODUCTS_ENDPOINT}`); } } @@@@ (4) Create Product-List Component $ ng g c components/product-list export class ProductListComponent implements OnInit { products: Product[] = []; constructor(private productService: ProductService) {} ngOnInit(): void { this.getAllProducts(); } getAllProducts() { this.productService.getAllProducts().subscribe(res => { this.products = res.data; }) } } @@@@ (5) Write Presentation Logic to Display Products Data in template (product-list.component.html)

{{tempProduct.name}} : {{tempProduct.unitPrice}}

@@@@ (6) invoke product-list component from AppComponent using selector. @@@@ (7)) When we run angular app, we should see products data in browser. ====================================================== Step-3 : Beautify Products Display in Table Format ====================================================== @@@@ (1) Add bootstap link in index.html file EcommUi @@@@ (2) Make below changes in app.component.html file

Welcome to Ashok IT - Ecommerce Store

@@@@ (3) Make below changes in product.list.component.html file
Name Price Units in Stock
No products available
{{tempProduct.name}} {{tempProduct.unitPrice}} {{tempProduct.unitsInStock}}
=========================================== Step-4 : eCommerce Template Integration =========================================== @@@@ (1) Download below zip file which contains images and styles.css URL : https://github.com/ashokitschool/Images.git @@@@( 2) Keep images folder in "angular project public folder" and keep "styles.css" file in src folder. @@@@ (3) Install bootstrap $ npm install bootstrap@5.2.0 @@@@ (4) Install FontAwesome $ npm install @fortawesome/fontawesome-free @@@@ (5) Verify installation entries in Node_Modules folder @@@@ (6) Add Custom Styles in "angular.json" file "styles": [ "src/styles.css", "node_modules/bootstrap/dist/css/bootstrap.min.css", "node_modules/@fortawesome/fontawesome-free/css/all.min.css" ], @@@@ (7) Modify app.component.html file @@@@ (8) Modify product-list.component.html file to display products in grid format.

{{ tempProduct.name }}

{{ tempProduct.unitPrice }}
Add to cart
========================================= Step - 5 : Filter Products By Category ========================================= 1) Configure Routings in app.routes.ts file export const routes: Routes = [ {path: 'category/:id', component: ProductListComponent}, {path: 'products', component: ProductListComponent}, {path: '', redirectTo: '/products', pathMatch: 'full'} ]; 2) Configure routerlink in app.component.html file (hardcoded links) 3) Configure in app.component.html file (remove selector to invoke product-list component). 4) Create a method in product.service.ts file to get products based on given category id. getProductsByCategoryId(theCategoryId: number): Observable { return this.http.get(`${AppConstants.PRODUCTS_ENDPOINT}/${theCategoryId}`); } 5) Make changes to ProductList component to read categoryId from routerLink export class ProductListComponent implements OnInit { products: Product[] = []; currentCategoryId = 1; constructor(private productService: ProductService, private route: ActivatedRoute ) { } ngOnInit(): void { this.route.paramMap.subscribe(() => { this.getAllProducts(); }) } getAllProducts() { // check if "id" param is available const hasCategoryId: boolean = this.route.snapshot.paramMap.has("id"); if (hasCategoryId) { this.currentCategoryId = +this.route.snapshot.paramMap.get("id")!; } else { this.currentCategoryId = 1; } this.productService.getProductsByCategoryId(this.currentCategoryId).subscribe(res => { this.products = res.data; }) } } =================================================== Step - 5 : Build Product Category Menu Dynamically =================================================== @@@@ (1) Create ProductCategory class to map backend-api response $ ng g class dto/product-category export class ProductCategory { constructor(public id: number, public categoryName: string){} } @@@@ (2) update product-service class to make backend call for category menu data getAllCategories(): Observable { return this.http.get(`${AppConstants.CATEGORY_ENDPOINT}`); } @@@@ (3) Create product-category-menu component $ ng generate component components/product-category-menu export class ProductCategoryMenuComponent implements OnInit{ productCategories: ProductCategory[] = []; constructor(private productService: ProductService){} ngOnInit(): void { this.listProductCategories(); } listProductCategories(){ this.productService.getAllCategories().subscribe(res => { this.productCategories = res.data; }) } } @@@@ (4) Write Product-Category template logic to display menu @@@@ (5) Remove static menu from app-component.html file and invoke product-category-menu using selector. ======================================== Step - 6 : Build Search Functionality ======================================== @@@@ (1) Configure routing in app.routes.ts file for search functionality {path: 'search/:keyword', component: ProductListComponent}, @@@@ (2) Create search component $ ng generate component components/search export class SearchComponent { constructor(private router: Router) { } doSearch(value: string) { this.router.navigateByUrl(`/search/${value}`); } } @@@@ (3) Write presentation logic for search component
@@@@ (4) Invoke search component from app.component.html file @@@@ (5) Create method in service class to handle search functionality backend api call searchProducts(theKeyword: string): Observable { return this.http.get(`${AppConstants.PRODUCTS_SEARCH_ENDPOINT}/${theKeyword}`); } @@@@ (6) Handle search operation in product-list-component.html // check keyword presence in url // if keyword present, call handle-search-products() with keyword // if keyword not present, call handle-list-products() with category-id export class ProductListComponent implements OnInit { products: Product[] = []; currentCategoryId = 1; searchMode: boolean = false; constructor(private productService: ProductService, private route: ActivatedRoute ) { } ngOnInit(): void { this.route.paramMap.subscribe(() => { this.listProducts(); }) } listProducts(){ this.searchMode = this.route.snapshot.paramMap.has("keyword"); if(this.searchMode){ this.handleSearchProducts(); }else{ this.handleListProducts(); } } handleListProducts() { // check if "id" param is available const hasCategoryId: boolean = this.route.snapshot.paramMap.has("id"); if (hasCategoryId) { this.currentCategoryId = +this.route.snapshot.paramMap.get("id")!; } else { this.currentCategoryId = 1; } this.productService.getProductsByCategoryId(this.currentCategoryId).subscribe(res => { this.products = res.data; }) } handleSearchProducts(){ const theKeyword:string = this.route.snapshot.paramMap.get("keyword")!; this.productService.searchProducts(theKeyword).subscribe(res => { this.products = res.data; }) } } ======================================== Step - 7 : Shopping Cart ======================================== @@@@ (1) Create CartItem class export class CartItem { id: number; name: string; imageUrl: string; unitPrice: number; quantity: number; constructor(product: Product) { this.id = product.id; this.name = product.name; this.imageUrl = product.imageUrl; this.unitPrice = product.unitPrice; this.quantity = 1; } } @@@@ (2) Create CartStatus component $ ng g c cart-status @@@@ (3) Remove hard coded cart details and invoke cart-status component from app.component.html
@@@@ (4) Create CartService to handle cart related business logic $ ng generate service services/cart export class CartService { totalPrice: Subject = new Subject(); totalQuanity: Subject = new Subject(); cartItems: CartItem[] = []; constructor() { } addToCart(theCartItem: CartItem) { let alereadyExistsInCart: boolean = false; let existingCartItem!: CartItem; if (this.cartItems.length > 0) { // check item presence is cart based on item id for (let tempCartItem of this.cartItems) { if (tempCartItem.id === theCartItem.id) { existingCartItem = tempCartItem; alereadyExistsInCart = true; break; } } } if (alereadyExistsInCart) { existingCartItem.quantity++; } else { this.cartItems.push(theCartItem); } //compute cart values this.computeCartTotals(); } computeCartTotals() { let totalPriceValue: number = 0; let totalQuantityValue: number = 0; for (let currentCartItem of this.cartItems) { totalPriceValue += currentCartItem.quantity * currentCartItem.unitPrice; totalQuantityValue += currentCartItem.quantity; } // publish new values for all subscribers this.totalPrice.next(totalPriceValue); this.totalQuanity.next(totalQuantityValue); } } @@@@ (5) add a function to handle 'Add To Cart' button in product-list-component // change in ts file addToCart(theProduct: Product) { console.log(`Adding to cart: ${theProduct.name}, ${theProduct.unitPrice}`); const theCartItem = new CartItem(theProduct); this.cartService.addToCart(theCartItem); } // change in template Add to cart @@@@ (6) Make changes in cart-status-component to display latest cart values (price & quantity) // component ts file change export class CartStatusComponent implements OnInit { totalPrice: number = 0; totalQuantity: number = 0; constructor(private cartService: CartService) { } ngOnInit(): void { this.updateCartStatus(); } updateCartStatus() { this.cartService.totalPrice.subscribe( data => this.totalPrice = data ); this.cartService.totalQuanity.subscribe( data => this.totalQuantity = data ); } } // template change
{{ totalPrice }} {{ totalQuantity }}
================================ Step - 7 : Cart Details Page ================================ @@@@ (1) Create cart-details component $ ng g c components/cart-details @@@@ (2) Configure routing {path: 'cart-details', component:CartDetailsComponent}, @@@@ (3) Change cart-status template to invoke cart-details page @@@@ (3) Write logic in cart-details component to access cart-items // ts file changes export class CartDetailsComponent implements OnInit{ cartItems: CartItem[] = []; totalPrice: number = 0; totalQuantity: number = 0; ngOnInit(): void { this.listCartDetails(); } constructor(private cartService: CartService) { } listCartDetails(){ this.cartItems = this.cartService.cartItems; // subscribe cart price this.cartService.totalPrice.subscribe( data => this.totalPrice = data ); // subscribe total-quantity this.cartService.totalQuanity.subscribe( data=> this.totalQuantity = data ); this.cartService.computeCartTotals(); } } @@@@ (4) Write presentation logic to display cart-details
Product Image Product Detail

{{ tempCartItem.name }}

{{ tempCartItem.unitPrice }}

{{ tempCartItem.quantity }}

Subtotal: {{ tempCartItem.quantity * tempCartItem.unitPrice }}

Total Quantity: {{ totalQuantity }}

Shipping: FREE

Total Price: {{ totalPrice }}

@@@@ (5) Add functionality to increment & Decrement item quantity in cart-service decrementQuantity(theCartItem: CartItem) { theCartItem.quantity--; if (theCartItem.quantity == 0) { this.remove(theCartItem); } else { this.computeCartTotals(); } } remove(theCartItem: CartItem) { // get index of item in the array const itemIndex = this.cartItems.findIndex(tempCartItem => tempCartItem.id === theCartItem.id); if(itemIndex > -1){ this.cartItems.splice(itemIndex, 1); this.computeCartTotals(); } } @@@@ (6) Write functions in cart-details component to handle increment, decrement and remove buttions incrementQuantity(theCartItem: CartItem) { this.cartService.addToCart(theCartItem); } decrementQuantity(theCartItem:CartItem){ this.cartService.decrementQuantity(theCartItem); } removeItem(theCartItem: CartItem){ this.cartService.remove(theCartItem); } 7) Make changes in cart-details template page to display +, - and remove options
Product Image Product Detail

{{ tempCartItem.name }}

{{ tempCartItem.unitPrice }}

{{ tempCartItem.quantity }}

Subtotal: {{ tempCartItem.quantity * tempCartItem.unitPrice | currency: 'USD' }}

Cart Summary

Total Quantity: {{ totalQuantity }}

Total Price: {{ totalPrice | currency: 'USD'}}

================================ Step - 8 : Checkout Page ================================ @@@@ (1) Generate checkout component $ ng generate component components/checkout @@@@ (2) Configure router for checkout component {path: 'checkout', component:CheckoutComponent}, @@@@ (3) Import RouterModule in cart-details component Note: Make sure checkout button having router link to load checkout component. @@@@ (4) Configure Form Group in checkout component ts file to load form fields export class CheckoutComponent implements OnInit { checkoutFormGroup!: FormGroup; totalPrice: number = 0; totalQuantity: number = 0; constructor(private formBuilder: FormBuilder) { } ngOnInit(): void { this.checkoutFormGroup = this.formBuilder.group({ customer: this.formBuilder.group({ name: [''], email: [''], phno: [''] }), shippingAddress: this.formBuilder.group({ street: [''], city: [''], state: [''], hno: [''], zipCode: [''] }), }) } onSubmit(){ console.log(this.checkoutFormGroup.get('customer')!.value); console.log(this.checkoutFormGroup.get('shippingAddress')!.value); } } 5) Presentation logic to display form in checkout template page

Customer

Shipping Address

Review Your Order

Total Quantity: {{ totalQuantity }}

Shipping: FREE

Total Price: {{ totalPrice }}

============================================== Step - 9 : Sending Order details to backend ============================================== 1) Create binding classes ng generate class dto/customer ng generate class dto/address ng generate class dto/order ng generate class dto/orderitems ng generate class dto/purchaseorder export class Customer { constructor(public name: string, public email: string, public phno: string) { } } export class Address { constructor(public street: string, public city: string, public state: string, public houseNum: string, public zipCode: string) { } } export class Order { constructor (public totalQuantity: number, public totalPrice: number) { } } export class Orderitems { constructor(public imageUrl: string, public unitPrice: number, public quantity: number, public id: number) { } } export class Purchaseorder { customer!: Customer; address!: Address; order!: Order; orderItems!: Orderitems[] } 2) Create checkout service to handle order business logic export class CheckoutService { constructor(private httpClient: HttpClient) { } placeOrder(purchase: Purchaseorder): Observable { return this.httpClient.post(AppConstants.ORDER_ENDPOINT, purchase); } } 3) Make changes in checkout component ts file to send order details to backend export class CheckoutComponent implements OnInit { checkoutFormGroup!: FormGroup; totalPrice: number = 0; totalQuantity: number = 0; constructor(private formBuilder: FormBuilder, private cartService: CartService, private checkoutService: CheckoutService ) { } ngOnInit(): void { this.reviewCartDetails() this.checkoutFormGroup = this.formBuilder.group({ customer: this.formBuilder.group({ name: [''], email: [''], phoneNo: [''] }), address: this.formBuilder.group({ street: [''], city: [''], state: [''], houseNum: [''], zipCode: [''] }), }) } get getName() { return this.checkoutFormGroup.get('customer.name'); } get email() { return this.checkoutFormGroup.get('customer.email'); } get phno() { return this.checkoutFormGroup.get('customer.phno'); } get addressStreet() { return this.checkoutFormGroup.get('address.street'); } get addressCity() { return this.checkoutFormGroup.get('address.city'); } get adressState() { return this.checkoutFormGroup.get('address.state'); } get addressZipCode() { return this.checkoutFormGroup.get('address.zipCode'); } get addressHouseNum() { return this.checkoutFormGroup.get('address.houseNum'); } onSubmit() { // setup order let order = new Order(this.totalQuantity, this.totalPrice); // setup order items const cartItems = this.cartService.cartItems; let orderItems: Orderitems[] = cartItems.map(tempCartItem => new Orderitems(tempCartItem.imageUrl!, tempCartItem.unitPrice!, tempCartItem.quantity, tempCartItem.id!)); // setup purchase order let purchase = new Purchaseorder(); // set customer data to purchase order purchase.customer = this.checkoutFormGroup.controls['customer'].value; // set address data to purchase order purchase.shippingAddress = this.checkoutFormGroup.controls['address'].value; // set order purchase.order = order; // set order items purchase.orderItems = orderItems; // make backend api call this.checkoutService.placeOrder(purchase).subscribe(response => { const responseData = response.data; console.log(responseData); // Payment Gateway configuration }) } reviewCartDetails() { this.cartService.totalPrice.subscribe( data => this.totalPrice = data ); this.cartService.totalQuantity.subscribe( data => this.totalQuantity = data ); } } 4) Make changes in template to display checkout form

Customer

Shipping Address

Review Your Order

Total Quantity: {{ totalQuantity }}

Shipping: FREE

Total Price: {{ totalPrice }}

============================================== Step - 9 : Payment Gateway Integration ============================================== 1) Configure razorpay java script file in index.html 2) create a file under src (filename: razorpay.d.ts) to declare razorpay object declare var Razorpay: any; 3) Create payment service to handle razorypay gateway export class PaymentService { private RAZORPAY_KEY = 'rzp_test_sIueffgdgb15KX'; // Replace with your Razorpay key constructor() { } processPayment(orderId: string, amount: number, successCallback: (response: any) => void): void { const options: any = { key: this.RAZORPAY_KEY, amount: amount * 100, currency: 'INR', name: 'Ashok IT', description: 'Ecommerce order', order_id: orderId, handler: successCallback, prefill: { name: 'Ashok IT', email: 'info@ashokit.in', contact: '9797979' }, notes: { addres: 'Customer Address' }, theme: { "color": "#3399cc" } }; const rzp1 = new Razorpay(options); rzp1.open(); } } 4) After Creating order in backend, open payment gateway page using payment service method // changes in checkout component ts file export class CheckoutComponent implements OnInit { checkoutFormGroup!: FormGroup; totalPrice: number = 0; totalQuantity: number = 0; constructor(private formBuilder: FormBuilder, private cartService: CartService, private checkoutService: CheckoutService, private paymetService: PaymentService ) { } ngOnInit(): void { this.reviewCartDetails() this.checkoutFormGroup = this.formBuilder.group({ customer: this.formBuilder.group({ name: [''], email: [''], phoneNo: [''] }), address: this.formBuilder.group({ street: [''], city: [''], state: [''], houseNum: [''], zipCode: [''] }), }) } get getName() { return this.checkoutFormGroup.get('customer.name'); } get email() { return this.checkoutFormGroup.get('customer.email'); } get phno() { return this.checkoutFormGroup.get('customer.phoneNo'); } get addressStreet() { return this.checkoutFormGroup.get('address.street'); } get addressCity() { return this.checkoutFormGroup.get('address.city'); } get adressState() { return this.checkoutFormGroup.get('address.state'); } get addressZipCode() { return this.checkoutFormGroup.get('address.zipCode'); } get addressHouseNum() { return this.checkoutFormGroup.get('address.houseNum'); } onSubmit() { // setup order let order = new Order(this.totalQuantity, this.totalPrice); // setup order items const cartItems = this.cartService.cartItems; let orderItems: Orderitems[] = cartItems.map(tempCartItem => new Orderitems(tempCartItem.imageUrl!, tempCartItem.unitPrice!, tempCartItem.quantity, tempCartItem.id!)); // setup purchase order let purchase = new Purchaseorder(); // set customer data to purchase order purchase.customer = this.checkoutFormGroup.controls['customer'].value; // set address data to purchase order purchase.address = this.checkoutFormGroup.controls['address'].value; // set order purchase.order = order; // set order items purchase.orderItems = orderItems; // make backend api call this.checkoutService.placeOrder(purchase).subscribe(response => { const responseData = response.data; console.log(responseData); this.paymetService.processPayment(response.razorpayOrderId, this.totalPrice, this.onPaymentSuccess.bind(this)); }) } onPaymentSuccess(response: any){ console.log("payment success"); alert("payment-success"); } reviewCartDetails() { this.cartService.totalPrice.subscribe( data => this.totalPrice = data ); this.cartService.totalQuantity.subscribe( data => this.totalQuantity = data ); } }