'How to iterate over an array with map or forEach in node express with the HBS template engine and return it in my .hbs file?

[when iterating over objects I have no problem, but I have no way or idea of how to do it with the arrays, I need that after filling in the data of my form when clicking on send, it takes me to my page with the route /products (which until now that works) but I need it to show me a list of all the products that I am adding, and it only shows me a list with a single product, I keep adding products and it replaces the singular product that was there before, therefore I cannot keep all the products]

[INDEX.JS]

const express = require("express");
const routes = require("./routes");
const path = require("path");
let hbs = require('hbs')
const app = express();
const port = 8080;
const { customAlphabet } = require("nanoid");
const nanoid = customAlphabet("1234567890", 5);

hbs.localsAsTemplateData(app);

app.set("view engine", "hbs");
app.use(express.static(__dirname + "public"));
app.use(express.json());
app.use(express.urlencoded({ extended: true }));

app.get("/", (req, res) => {
  res.render("form", {
    title: "Productos",
  });
});

// app.locals.titulo = "Mi tienda";

app.post("/productos", (req, res) => {
  if (!req.body.title && !req.body.price && !req.body.thumbnail) {
    res.status(400).json({
      error: "Bad Request",
      message: "Title, price and thumbnail are required",
    });
  }
  const body = req.body;
  const producto = [
    {
      id: Number(nanoid(5)),
      title: body.title,
      price: body.price,
      thumbnail: body.thumbnail,
    },
  ];
  console.log(producto);
 return res.render("productos", {
    producto,
  });
});

app.get("/productos", (req, res) => {
  res.render("form", {
    title: "Productos",
  });
});

routes(app);

app
  .listen(port, () => {
    console.log(`App listening on port ${port}!`);
  })
  .on("error", (err) => {
    console.log(err);
    throw err;
  });

[FORM.JS]

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <meta name="viewport" content="width=device-width, initial-scale=10" />
    <title>Api Shoes Rest</title>
    <style>
      input::-webkit-outer-spin-button, input::-webkit-inner-spin-button {
      -webkit-appearance: none; margin: 0; } input[type=number] {
      -moz-appearance: textfield; }
    </style>
  </head>
  <body>
    <h1 style="font-family: arial; text-align: center">Ingresá tu producto!</h1>
    <form
      action="/productos"
      method="POST"
      style="
        display: flex;
        flex-direction: column;
        text-align: center;
        font-size: 21px;
        font-weight: bold;
        font-family: Arial, Helvetica, sans-serif;
        align-items: center;
        justify-content: center;
        margin: 5px 0;
      "
    >
      <label for="title">
        <input
          type="text"
          name="title"
          id="title"
          placeholder="Title"
          style="
            margin-bottom: 10px;
            border: 1px solid #ccc;
            outline: none;
            border-radius: 5px;
            padding: 8px;
          "
          required
        />
      </label>

      <label for="price">
        <input
          placeholder="Price"
          name="price"
          id="price"
          style="
            margin-top: 10px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            outline: none;
            border-radius: 5px;
            padding: 8px;
          "
          pattern="[0-9.]+"
          type="number"
          required
        />
      </label>
      <label for="thumbnail">
        <input
          type="url"
          pattern="https?://.+"
          title="Include http://"
          placeholder="Thumbnail(URL)"
          name="thumbnail"
          id="thumbnail"
          style="
            margin-top: 10px;
            margin-bottom: 10px;
            border: 1px solid #ccc;
            outline: none;
            border-radius: 5px;
            padding: 8px;
          "
          required
        />
      </label>
      <button
        style="
          padding: 8px 70px;
          font-weight: 700;
          text-align: center;
          font-size: 15px;
          letter-spacing: 1px;
          max-width: 200px;
          cursor: pointer;
          font-family: Helvetica, sans-serif;
          border: 1px solid #ccc;
          border-radius: 5px;
          background-color: #ccc;
          margin-top: 10px;
          text-transform: uppercase;
        "
        type="submit"
      >
       SEND
      </button>
    </form>
    <script>
      document.querySelector("form").addEventListener("input", function (e) {
      const tgt = e.target; if (tgt.type && tgt.type === "number") { const val =
      tgt.value; const nums = val.replace(/[^\d.-]/g, ""); if (!/\d+/.test(val))
      tgt.value = ""; } });
    </script>
  </body>
</html>

[PRODUCTOS.HBS]

<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta http-equiv="X-UA-Compatible" content="IE=edge" />
    <link
      rel="stylesheet"
      href="https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css
"
    />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Productos</title>
  </head>
  <body class="container form-control-plaintext">
    <div class="container">
      <div class="row">
        <div class="col-12">
          <h1>Productos</h1>
        </div>
      </div>
    <table class="table">
      <thead class="table-dark">
        <tr>
          <th class="text-center" scope="col">#</th>
          <th class="text-center" scope="col">Product</th>
          <th class="text-center" scope="col">Price</th>
          <th class="text-center" scope="col">Preview</th>
        </tr>
      </thead>
      <tbody>
        {{#each producto}}
        <tr>
          <th class="text-center" scope="row">{{id}}</th>
          <td class="text-center">{{title}}</td>
          <td class="text-center">{{price}}</td>
          <td class="text-center">
              <img
              src={{thumbnail}}
              alt="535353"
              style="width:100%; height: 184px; margin-left: 9px;"
            />
          </td>
        </tr>
        {{/each  }}
      </tbody>
    </table>
    <button style="outline: none; height: 55px; text-align: center; float: left; font-weight: bold; font-size: 26px; border: none; background-color: rgb(0, 0, 0); color: rgb(255, 255, 255);">
      Go to home
    </button>
    <script
      src="https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js"
    ></script>
  </body>
</html>

[API.JS]

const { customAlphabet } = require("nanoid");
const nanoid = customAlphabet("1234567890", 5);
class Productos {
  constructor() {
    this.productos = [];
  }

  obtenerProductos() {
    return this.productos;
  }

  obtenerProducto(id) {
    return this.productos.find((producto) => producto.id === Number(id));
  }

  create(data) {
    const newProduct = {
      ...data,
      id: Number(nanoid(5)),
    };
    this.productos.push(newProduct);
    return newProduct;
  }

  eliminarProducto(id) {
    this.productos = this.productos.filter(
      (producto) => producto.id !== Number(id)
    );
  }

  actualizarProducto(id, data) {
    const producto = this.obtenerProducto(id);
    if (producto) {
      Object.assign(producto, data);
      return producto;
    }
    throw new Error();
  }
}

module.exports = Productos;

[PRODUCTSROUTER.JS]

const express = require("express");
const Productos = require("../api");

const router = express.Router();
const itemService = new Productos();

router.get("/", (req, res) => {
  res.render("form", {
    title: "Productos",
  })
});

router.post("/", (req, res) => {
  if (!req.body.title && !req.body.price && !req.body.thumbnail) {
    res.status(400).json({
      error: "Bad Request",
      message: "Title, price and thumbnail are required",
    });
  }
  const producto = itemService.create(req.body);
  res.json(producto);
});

router.get("/:id", (req, res) => {
  const producto = itemService.obtenerProducto(req.params.id);
  if (producto) {
    res.json(producto);
  } else {
    res.status(404).json({ error: "Producto no encontrado" });
  }
});

router.put("/:id", (req, res) => {
  const id = req.params.id;
  const producto = itemService.actualizarProducto(id, req.body);
  res.json(producto);
});

router.delete("/:id", (req, res) => {
  const id = req.params.id;
  itemService.eliminarProducto(id);
  res.json({ message: "Producto eliminado" });
});

module.exports = router;


Sources

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

Source: Stack Overflow

Solution Source