Search code examples
jsonangularngfor

Cannot use *ngFor for a two-dimensional array in Angular


I saw many similar questions but the solutions there didn't work for me. I want to display orders in Angular.

I receive clients' orders in Json format from Spring. Like this:

[
    {
        "id": 1,
        "orderProducts": [],
        "numberOfProducts": 0
    },
    {
        "id": 2,
        "orderProducts": [
            {
                "quantity": 4,
                "product": {
                    "id": 1,
                    "brand": "apple",
                    "name": "iphone11",
                    "categoryId": 1
                }
            }
        ],
        "numberOfProducts": 1
    },
    {
        "id": 3,
        "orderProducts": [
            {
                "quantity": 9,
                "product": {
                    "id": 1,
                    "brand": "apple",
                    "name": "iphone11",
                    "categoryId": 1
                }
            },
            {
                "quantity": 6,
                "product": {
                    "id": 2,
                    "brand": "xiaomi",
                    "name": "Note10",
                    "categoryId": 1
                }
            },
            {
                "quantity": 1,
                "product": {
                    "id": 6,
                    "brand": "cccccccccccccccc",
                    "name": "cccccccccccccc",
                    "categoryId": 1
                }
            }
        ],
        "numberOfProducts": 3
    },
    {
        "id": 4,
        "orderProducts": [
            {
                "quantity": 5,
                "product": {
                    "id": 1,
                    "brand": "apple",
                    "name": "iphone11",
                    "categoryId": 1
                }
            }
        ],
        "numberOfProducts": 1
    }
]

So i created a class in Angular to accept it.

db-orders.ts

export class DbOrders {
    id: number;
    orders: ProductOrder[];
    numberOfProducts: number;

    constructor(orders: ProductOrder[]){
        this.orders = orders;
    }
}

product-order.ts

export class ProductOrder {
    product: Product;
    quantity: number;

    constructor(product: Product, quantity: number){
        this.product = product;
        this.quantity = quantity;
    }
}

product.ts

export class Product {
    id: number;
    brand: string;
    name: string;
    category: ProductCategory;

    constructor(){}

}

Here is a service class.

order.service.ts

export class OrderService {

  private orderUrl: string = 'http://localhost:8080/orders';

  constructor(private http: HttpClient) { }

  saveOrder(order: ProductOrders) {
    console.log("Hello")
    return this.http.post<ProductOrders>(this.orderUrl, order);
}

public findOrders(): Observable<DbOrders[]> {
  return this.http.get<DbOrders[]>(this.orderUrl);
}

}

order-list.component.ts

export class OrderListComponent implements OnInit {

  receivedOrders: DbOrders[] = [];
 
  constructor(private orderService: OrderService) { }

  ngOnInit(): void {
    this.orderService.findOrders().subscribe(
      data =>{
        this.receivedOrders = data;
      }
    );
  }
}

order-list.component.html

<div *ngFor="let receivedOrder of receivedOrders">
   <p>Number of Products in the order: {{receivedOrder.numberOfProducts}}</p> 
 
     <div *ngFor="let order of receivedOrder.orders">
        <h1>Product name: {{ order.product.name }}</h1>
    </div>
</div>

In this case, only the number of products is displayed,nothing else:

Screenshot

I tried to add a toArray method:

<div *ngFor="let order of toArray(receivedOrder.orders)">


ngOnInit(): void {
    this.orderService.findOrders().subscribe(
      data =>{
        this.receivedOrders = data;
      }
    );
  }

  toArray(orders: object) {
    return Object.keys(orders).map(key => orders[key])
  }

Doesn't work.

Also i tried to add indexes.

<div *ngFor="let receivedOrder of receivedOrders; let i=index"">
       <p>Number of Products in the order: {{receivedOrder.numberOfProducts}}</p> 
     
         <div *ngFor="let order of receivedOrder.orders; let j=index"">
            <h1>Product name: {{ order.product.name }}</h1>
        </div>
    </div>

Doesn't work either.

What is my mistake? Thank you!


Solution

  • You seem to have a few problems. Firstly, your data structure does not match your classes.

    E.g. your ProductOrder object should have an array of OrderProduct objects as that is what your data has.

    Your constructor for ProductOrder never gets called because you are no instantiating the object from the class.

    In simple terms, change your html to this for it to work:

    <div *ngFor="let receivedOrder of data; let i=index">
        <p>Number of Products in the order: {{receivedOrder.numberOfProducts}}</p>
    
        <div *ngFor=" let order of receivedOrder.orderProducts; let j=index">
            <h1>Product name: {{ order.product.name }}</h1>
        </div>
    </div>
    

    You can also find a demo on StackBlitz.