0

our professor asks us to build a simple e-commerce app with the following components: customers, orders, menu (a navbar) and a cart. We need to display the orders in the front-end depending on the selected customer who is selected from a dropdown menu in the navbar, as the image illustrates:

Orders page

Once we select a customer its Id is assigned to a variable and then it's used to load the orders based on the customer id (thanks to an endpoint called getOrdersByCustomerId) and then it builds a card for each one. So far so good. The problem is that the function is called only once in the order component with the initial value of 0 and we don't know how to make a dynamic call every time the id changes. Here is the code:

  • Menu service (where the customer id is selected)
import { Injectable } from '@angular/core';
import { OrdersService } from './orders.service';

@Injectable({
  providedIn: 'root'
})
export class MenuService {

  constructor() { }

  customerId: number = 0;

  onCustomerChange(event: Event) {
    this.customerId = Number((<HTMLInputElement> event.target).value);

    console.log(this.customerId);
  }
}
  • Menu component
import { HttpClient } from '@angular/common/http';
import { Component, EventEmitter, OnInit, Output } from '@angular/core';
import { CustomerService } from 'src/services/customer.service';
import { MenuService } from 'src/services/menu.service';

@Component({
  selector: 'app-menu',
  templateUrl: './menu.component.html',
  styleUrls: ['./menu.component.css']
})
export class MenuComponent implements OnInit {

  constructor(public customerService: CustomerService, public menuService: MenuService) {
    this.customers = this.customerService.getCustomers();
    console.log(this.customers);
  }

  ngOnInit(): void {

  }

  title: string = 'E-commerce';

  customers: {
    id: number,
    name: string,
    surname: string
  }[] = [];

  customer!: {
    id: number,
    name: string,
    surname: string
  };

  links = [
    {name: 'Products', href: '/products', disabled: false},
    {name: 'Orders', href: '/orders', disabled: false},
    {name: 'Cart', href: '/cart', disabled: true},
  ];

  onCustomerChange(event: Event) {
    this.menuService.onCustomerChange(event);
  }
}
  • HTML Menu component
<nav class="navbar navbar-expand-lg bg-primary">
 <div class="container-fluid">
   <a class="navbar-brand" routerLink="">{{ title }}</a>
   <button class="navbar-toggler" type="button" data-bs-toggle="collapse" data-bs-target="#navbarSupportedContent" aria-controls="navbarSupportedContent" aria-expanded="false" aria-label="Toggle navigation">
     <span class="navbar-toggler-icon"></span>
   </button>
   <div class="collapse navbar-collapse" id="navbarSupportedContent">
     <ul class="navbar-nav me-auto mb-2 mb-lg-0">
       <li class="nav-item" *ngFor="let link of links">
         <a class="nav-link" aria-current="page" routerLink="{{ link.href }}" routerLinkActive="active">{{ link.name }}</a>
       </li>
     </ul>
   </div>
   <div class="d-flex">
     <select (change)="onCustomerChange($event)" class="form-select" aria-label="Customer select">
       <option value="0">Select customer</option>
       <option *ngFor="let customer of customers" value="{{ customer.id }}" >{{ customer.name }} {{ customer.surname }}</option>
     </select>
   </div>
 </div>
</nav>
<!-- [ngModel]="customer.id"
{{ customer.id }}" (change)="-->
  • Orders service (where we need the id to display the orders)
import { HttpClient } from '@angular/common/http';
import { Injectable, OnChanges, OnInit, SimpleChanges } from '@angular/core';
import { CartService } from './cart.service';
import { CustomerService } from './customer.service';
import { MenuService } from './menu.service';

@Injectable({
  providedIn: 'root'
})
export class OrdersService implements OnInit {

  constructor(
    public httpClient: HttpClient,
    public cartService: CartService,
    public customerService: CustomerService,
    public menuService: MenuService) {
      this.loadOrdersByClientId(this.menuService.customerId);
  }

  ngOnInit(): void {
    this.loadOrdersByClientId(this.menuService.customerId);
  }

  private orders: {
    id: number,
    products: any,
    amount: number,
    customer: any
  }[] = [];

  loadOrdersByClientId(id: number) {

    this.httpClient.get(`http://localhost:8080/order/by-client-id/${id}`).subscribe((response) => {

      //console.log(id);

      Object.entries(response).forEach(([key, value]) => {
        this.orders.push(value);
      })


    })
  }

  getOrders() {
    return this.orders;
  }
}
  • Orders component
import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { OrdersService } from 'src/services/orders.service';

@Component({
  selector: 'app-orders',
  templateUrl: './orders.component.html',
  styleUrls: ['./orders.component.css']
})
export class OrdersComponent implements OnInit {

  constructor(public ordersService: OrdersService) {}

  ngOnInit(): void {
    this.orders = this.ordersService.getOrders();
  }

  orders: {
    id: number,
    products: any,
    amount: number,
    customer: any
  }[] = [];

}
  • HTML Orders component
<div class="card" style="width: 18rem;" *ngFor="let order of orders">
  <div class="card-body">
    <h5 class="card-title">{{order.id}}</h5>
    <h6 class="card-subtitle mb-2 text-muted">{{order.amount}}</h6>
  </div>
</div>

Probably there are a tons of bad practices in these scripts since we have been working with Angular for just 1 week, sorry for that. I leave the client component out for brevity since it is not related to the issue (it works, and the IDs are correctly retrieved). Thanks in advance for the help!

  • Don't know if we have all the relevant infos or if some functions are missing but from what I can see no actions others than assigning `customerId` is done when you change your selector. Don't know in version 15 but from what I remember because this property isn't bound anywhere in the HTML, Angular has no reason to trigger any change automatically to the view. The change of the id should at least trigger `customer` to change (even if it seems unused) and the orders too. Sorry it's been a long time it would take me time to find how you "listen" to a class property change – Kaddath Jan 02 '23 at 17:47

1 Answers1

1

It seems that when the customer is changed, only the customerId is updated and nothing else. One way to trigger a new backend call would be to emit an event when the customerId is updated. Then you can listen for it in the order component and trigger a new backend call.

In your menuservice:

import { Injectable, EventEmitter } from '@angular/core';
import { OrdersService } from './orders.service';

@Injectable({
  providedIn: 'root'
})
export class MenuService {

  public customerIdUpdate = new EventEmitter();

  constructor() { }

  customerId: number = 0;

  onCustomerChange(event: Event) {
    this.customerId = Number((<HTMLInputElement> event.target).value);
    this.customerIdUpdate.emit(); 
    console.log(this.customerId);
  }
}

And then you can subscribe to the event in your order component as follows:

import { ChangeDetectorRef, Component, OnInit } from '@angular/core';
import { OrdersService } from 'src/services/orders.service';
import { MenuService } from './menu.service';

@Component({
  selector: 'app-orders',
  templateUrl: './orders.component.html',
  styleUrls: ['./orders.component.css']
})
export class OrdersComponent implements OnInit {

  constructor(public ordersService: OrdersService, public menuService: MenuService) {}

  ngOnInit(): void {
    this.orders = this.ordersService.getOrders();
    this.menuService.customerIdUpdate.subscribe(()=>{
      this.orders = this.ordersService.getOrders();
    })
  }

  orders: {
    id: number,
    products: any,
    amount: number,
    customer: any
  }[] = [];

}
riorudo
  • 977
  • 7
  • 14