'How to call a function with data from foreach method in HTML string?

I am generating some html card and buttons from an array. I want to call a function with the data from the foreach. But I can't seem to figure it out.

I am getting the problem in the renderProducts() method.

/// <reference path="coin.ts" />
/// <reference path="product.ts" />
/// <reference path="productFactory.ts" />

enum VendingMachineSize {
  small = 6,
  medium = 9,
  large = 1,
}

class Cell {
  constructor(public product: CocoCola) {}
  stock: 3;
  sold: false;
}

class VendingMachine {
  private totalMoney = 0;
  private totalMoneyText = <HTMLSpanElement>document.getElementById("total-money");
  private containerElement = <HTMLDivElement>document.querySelector(".machine");
  allCoins: number[] = [0];
  cells = [];
  selectedCells = [new Cell(new CocoCola())];

  set size(givenSize: VendingMachineSize) {
    this.cells = [];

    for (let index = 0; index < givenSize; index++) {
      let product = ProductFactory.GetProduct();
      this.cells.push(new Cell(product));
    }
    this.renderProducts();
  }

  constructor() {
    console.log("I am vending machine!");
  }

  select(cell: Cell) {
    cell.sold = false;
    this.selectedCells.push(cell);
    console.log(this.selectedCells);
  }

  acceptCoin(coin: Quarter): void {
    this.totalMoney += coin.Value;
    this.totalMoneyText.textContent = this.totalMoney.toString();
  }

  renderProducts() {
    this.cells.forEach((product) => {
      let html = `<div class="card " style="width: 18rem">
        <img src=${product.product.category.getImageUrl()} class="" alt="..." />
        <div class="card-body">
          <h5 class="card-title">${product.product.name}</h5>
          <p class="card-text">
           ${product.product.description}
          </p>
          <button type="button" class="btn btn-outline-dark w-100 select-btn"  onclick="machine.select(${product})">💰 ${
        product.product.price
      }</button>
        </div>
      </div>`;
      this.containerElement.insertAdjacentHTML("beforeend", html);
    });
  }
}

<button type="button" class="btn btn-outline-dark w-100 select-btn" onclick="machine.select(${product})">💰 ${product.product.price}</button> I want this button to have an onclick listener with argument of product

When I do it like this it give me this error: Uncaught SyntaxError: Unexpected identifier (at (index):33:63)

This is where I created the instance of the class

/// <reference path="vendingMachine.ts" />

const machine = new VendingMachine();
machine.size = VendingMachineSize.medium;



Solution 1:[1]

You can not do it like that because you use string interpolation.

When you type

`some text ${product}`

and product is Object in your scope, javascript will call toString method on object and returns [Object object] <- here is the error that you recieved: **Uncaught SyntaxError: Unexpected identifier**

When you're trying to interpolate onclick handler, you should produce valid JS code, for example:

<div onclick="machine.select(${number})"></div>,
<div onclick="machine.select('${string}')"></div>,
<div onclick="machine.select(JSON.parse('${JSON.encode(product)}'))"></div>

I recommend set listener after generating html; For example:

<button type="button" data-product="${product.product.name}" class="btn btn-outline-dark w-100 select-btn">? ${product.product.price}</button>
...
this.containerElement.insertAdjacentHTML("beforeend", html);
this.containerElement.querySelectorAll('button').forEach(item => {
  item.addEventListener('click', () => {
    this.select(item.dataset.product)
  })
})

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Фарид Ахмедов