购物车中的产品重复

时间:2017-07-12 07:36:41

标签: javascript arrays ecmascript-6 javascript-objects

我的购物车功能有一个简单的问题。点击“添加到购物车”按钮后,如果它具有相同的产品ID,则会在新行中输出新产品。如果产品ID相同,则应该增加产品的数量。



const products = [];
const carts = [];
const inputs = {
  id: document.getElementById("productID"),
  desc: document.getElementById("product_desc"),
  qty: document.getElementById("quantity"),
  price: document.getElementById("price")
};
const productsTable = document.getElementById("products-table");
const cartsTable = document.getElementById("carts-table");

function renderProductsTable() {
  // delete all entries
  Array.from(productsTable.children).slice(1).forEach(entry => productsTable.removeChild(entry));
    
    for (product of products) {
      const tr = document.createElement('tr');
      const id = document.createElement('td');
      id.textContent = product.id;
      const desc = document.createElement('td');
      desc.textContent = product.desc;
      const qty = document.createElement('td');
      qty.textContent = product.qty;
      const price = document.createElement('td');
      price.textContent = product.price;
      const action = document.createElement('td');
      const deleteButton = document.createElement('button');
      deleteButton.textContent = 'Delete';
      deleteButton.addEventListener('click', () => removeProduct(product.id))
      const addToCartButton = document.createElement('button');
      addToCartButton.textContent = 'Add to cart';
      addToCartButton.addEventListener('click', () => addCart(product.id));
      action.appendChild(deleteButton);
      action.appendChild(addToCartButton);
      tr.appendChild(id);
      tr.appendChild(desc);
      tr.appendChild(qty);
      tr.appendChild(price);
      tr.appendChild(action);
      productsTable.appendChild(tr);
    }

}

function addProduct() {
  
  const product = {
  id: inputs.id.value,
  desc: inputs.desc.value, 
  qty: Number(inputs.qty.value),
  price: Number(inputs.price.value)
  };

  let existing = products.find(item => item.id === product.id);
  if (existing) {
  existing.qty += product.qty;
  } 
  else {
   products.push(product);
  }
  renderProductsTable();
  document.getElementById('order').reset();
  }



function removeProduct(product_id) {
  const index = products.findIndex(p => p.id === product_id);
  products.splice(index, 1);
  renderProductsTable();
}

function addCart(product_id) {
      const product = products.find(p => p.id === product_id);
      const cartItem = carts.find(c => c.product === product);
      
      if(cartItem) {
        cartItem.qty ++;
      }
      else {
        carts.push(product);
      }
      renderCartTable();
          
}

function renderCartTable() {
  for (cart of carts){
    const tr = document.createElement('tr');
    const id = document.createElement('td');
    id.textContent = cart.id;
    const desc = document.createElement('td');
    desc.textContent = cart.desc;
    const qty = document.createElement('td');
    qty.textContent = cart.qty;
    const price = document.createElement('td');
    price.textContent = cart.price;
    const total = document.createElement('td');
    total.textContent = cart.qty * cart.price
    const action = document.createElement('td');
    const subtractButton = document.createElement('button');
    subtractButton.textContent = 'Subtract Quantity';
    const addButton = document.createElement('button');
    addButton.textContent = 'Add Quantity';
    const removeButton = document.createElement('button');
    removeButton.textContent = 'Remove Item';
    tr.appendChild(id);
    tr.appendChild(desc);
    tr.appendChild(qty);
    tr.appendChild(price);
    tr.appendChild(total);
    tr.appendChild(action);
    cartsTable.appendChild(tr);
  }
}

<!DOCTYPE html>
<html>
<head>
  <title>Shopping Cart ES6</title>
</head>
<body>
  <form name="order" id="order">
    <table>
      <tr>
        <td>
          <label for="productID">Product ID:</label>
        </td>
        <td>
          <input id="productID" name="product" type="text" size="28" required/>
        </td>
      </tr>
      <tr>
          <td>
              <label for="product">Product Desc:</label>
          </td>
          <td>
              <input id="product_desc" name="product" type="text" size="28" required/>
          </td>
      </tr>
      <tr>
          <td>
              <label for="quantity">Quantity:</label>
          </td>
          <td>
              <input id="quantity" name="quantity" width="196px" required/>
          </td>
      </tr>
      <tr>
          <td>
              <label for="price">Price:</label>
          </td>
          <td>
              <input id="price" name="price" size="28" required/>
          </td>
      </tr>
  </table>
  <input type="reset" class="resetbtn" value="Reset" />
  <input type="button" id="btnAddProduct" onclick="addProduct();" value="Add New Product" >
</form>
<table border="1|1" id="products-table">
  <tr>
    <th>Product ID</th>
    <th>Product Description</th>
    <th>Quantity</th>
    <th>Price</th>
    <th>Action</th>
  </tr>
</table>
<br />
<h2>Shopping Cart</h2>
<table border="1|1" id="carts-table">
  <tr>
    <th>Product ID</th>
    <th>Product Description</th>
    <th>Quantity</th>
    <th>Price</th>
    <th>Total Amount</th>
    <th>Action</th>
  </tr>
</table>
</body>
<script src="script.js">
</script>
</html>
&#13;
&#13;
&#13;

2 个答案:

答案 0 :(得分:2)

因此,目前您的代码设置为在将所有产品添加到产品表时删除所有产品,但是在将它们添加到购物车时不会这样做。所以只需添加它就可以删除购物车表中的所有内容

Array.from(cartsTable.children).slice(1).forEach(entry => cartsTable.removeChild(entry));

但是您当前的代码存在一些小问题,nl:

  • 如果您添加两次相同的产品ID,则不会验证价格或描述是否相同
  • 如果您将产品添加到购物车表中,您只会将数量增加1,但是,产品本身可能会设置更高的数量,因此您可以像这样修复它

    function addCart(product_id) {
      const product = products.find(p => p.id === product_id);
      const cartItem = carts.find(c => c.product === product);
    
      if(cartItem) {
        cartItem.qty += product.qty;
      }
      else {
        carts.push(product);
      }
      renderCartTable();
    }
    
  • 您可以将<input type="number" />用于产品数量

  • 您可以将<input type="number" step="0.01" />用于价格字段
  • 从产品表中删除商品后,购物车中的产品将不再可用,因此您应该添加一个电话,以便从购物车中移除产品
  • 您有2个创建表的函数,两者都可以通用以共享相同的功能

我已经重写了你正在描述的函数,使用我在another question上已经给出的答案,它将根据给定的列和包含数据的数组创建表

在添加相同产品时,它仍然存在不会验证描述/价格差异的问题,但它有助于我提到的所有其他问题。

它可能是一个更长的代码,但是,部分原因是表助手功能可以做很多事情,并且可能对你描述的场景有点过分。但它确实有效,并且使表格创建更加容易;)

// will reassign when items get removed
let products = [];
// will reassign when items get removed
let cart = [];

function addOrIncrease(item, targetContainer, equality = (i) => i.id) {
  let match = targetContainer.find(i => equality(item) === equality(i));
  if (match) {
    // this could actually be a problem, eg: description and price are not validated
    // you might need to make sure that a warning pops up in case the price is different
    match.qty += item.qty;
  } else {
    // didn't find so it gets added to whichever container
    targetContainer.push(item);
  }
}

// Gets the value of the elementId or a defaultValue
function getValue( elementId, defaultValue ) {
  let elem = document.getElementById( elementId );
  if (!elem || !elem.value) {
    return defaultValue;
  }
  return elem.value;
}

// resets the value for an inputfield
function resetValue( elementId ) {
  let elem = document.getElementById( elementId );
  elem && (elem.value = null);
}

// adds a product to the list
function addProduct() {
  let product = {
    id: getValue('productId', ''),
    description: getValue('productDescription', ''),
    qty: parseInt(getValue('productQty', 1)),
    price: parseFloat(getValue('productPrice', 0))
  };
  if (product.id === '') {
    alert('Please enter a product id');
    return;
  }
  addOrIncrease( product, products );
  resetValue( 'productId' );
  resetValue( 'productDescription' );
  resetValue( 'productQty' );
  resetValue( 'productPrice' );
  renderProducts();
}

// adds an item to the cart
function addToCart(itemId) {
  var product = products.find( p => p.id === itemId );
  if (!product) {
    alert('Couldn\'t find product');
    return;
  }
  addOrIncrease( product, cart );
  renderCart();
}

// removes an item from the cart
function removeFromCart(itemId) {
  cart = cart.reduce( (current, item) => {
    if (item.id !== itemId) {
      current.push(item);
    }
    return current;
  }, []);
  renderCart();
}

// removes an item from the products list
// while simultanously removing it from the shopping cart (as it is no longer in the product list)
function removeFromProducts(itemId) {
  products = products.reduce( (current, item) => {
    if (item.id !== itemId) {
      current.push(item);
    }
    return current;
  }, []);
  renderProducts();
  // remove it from the cart, as it is no longer in the products list
  removeFromCart(itemId);
}

// renders the products to the table
// will re-render the full table each time
function renderProducts() {
  createTable('products', products, [{
      title: 'id',
      field: 'id',
      class: 'left'
    },
    {
      title: 'description',
      field: 'description',
      class: 'left'
    },
    {
      title: 'quantity',
      field: 'qty',
      class: 'right'
    },
    {
      title: 'price',
      field: 'price',
      class: 'right'
    },
    {
      title: 'total',
      value: (i) => i.price * i.qty,
      class: 'right',
      template: '%0 €'
    },
    {
      title: 'action',
      field: 'id',
      class: 'center',
      template: '<button type="button" onclick="removeFromProducts(\'%0\');">Remove product</button>' +
        '<button type="button" onclick="addToCart(\'%0\');">Add to cart</button>'
    }
  ]);
}

// renders the cart to the cart table
// will rerender each time called
function renderCart() {
  createTable('cart', cart, [{
      title: 'id',
      field: 'id',
      class: 'left'
    },
    {
      title: 'description',
      field: 'description',
      class: 'left'
    },
    {
      title: 'quantity',
      field: 'qty',
      class: 'right'
    },
    {
      title: 'price',
      field: 'price',
      class: 'right'
    },
    {
      title: 'total',
      value: (i) => i.price * i.qty,
      class: 'right',
      template: '%0 €',
      calculateTotal: true
    },
    {
      title: 'action',
      field: 'id',
      class: 'center',
      template: '<button type="button" onclick="removeFromCart(\'%0\');">Remove</button>'
    }
  ]);
}

/* Helper function to create a table dynamically */
/* Taken from: https://stackoverflow.com/questions/43924509/creating-an-html-table-using-javascript-and-json/43925208#43925208 */
function createTable(target, data, columns) {
  // gets the elements required based on id for the target div
  // and creates the table, thead, tbody & tfoot for the table
  let element = document.getElementById(target),
    table = document.createElement('table'),
    thead = document.createElement('thead'),
    header = document.createElement('tr'),
    tbody = document.createElement('tbody'),
    tfoot = document.createElement('tfoot'),
    // totals is used for the totals for the footer
    totals = {};

  // creates the header
  for (const column of columns) {
    // and creates the cells in the header, adding title and class
    let cell = document.createElement('td');
    cell.innerHTML = column.title;
    cell.className = column.class;
    header.appendChild(cell);
  }
  thead.appendChild(header);

  for (const item of data) {
    // creates the single rows
    let row = document.createElement('tr');
    for (const column of columns) {
      // and for each column creates the cell itself
      let cell = document.createElement('td');
      let value;
      // checks what to display
      if (column.field) {
        // only a property on the data
        value = item[column.field];
      } else if (column.value) {
        // a function with a callback value
        value = column.value(item)
      }
      // if it should calculate totals, it will do so here
      if (column.calculateTotal) {
        // in case the column is unknown, it's initialized as 0
        // warning: all values will be whole numbers
        totals[column.field] = (totals[column.field] || 0) + parseInt( value );
      }
      // if it has a template, we will replace the %0 with value
      // this template function supports only 1 value to be "templated"
      if (column.template) {
        value = column.template.split('%0').join(value);
      }
      // set the cell value
      cell.innerHTML = value;
      // set the class (used to align, for example)
      cell.className = column.class;
      // add cell to row
      row.appendChild(cell);
    }
    // add row to tbody
    tbody.appendChild(row);
  }
  // empty object would mean false, so only if totals needed to be calculated
  // would it create the footer here
  if (totals && data.length > 0) {
    let row = document.createElement('tr');
    for (const column of columns) {
      let cell = document.createElement('td'), value = '';
      if (column.calculateTotal) {
        value = totals[column.field];
        if (column.template) {
          // can still use the row template
          value = column.template.split('%0').join(value);
        }
      }
      cell.innerHTML = value;
      cell.className = column.class;
      row.appendChild( cell );
    }
    tfoot.appendChild( row );
  }
  table.appendChild(thead);
  table.appendChild(tbody);
  table.appendChild(tfoot);
  // clear the target element
  element.innerHTML = '';
  // set the table on the target element
  element.appendChild(table);
}

// start of the application, create the 2 tables
// and then it's up to the user
renderProducts();
renderCart();
.left {
  text-align: left;
}
.right {
  text-align: right;
}
thead tr {
  background-color: #777;
}
thead tr td {
  font-weight: bold;
  color: #fff;
}
tfoot tr td {
  font-weight: bold;
}
table td {
  padding: 5px;
  border-bottom: solid #efefef 1px;
}
.fields > div > span:first-child {
  display: inline-block;
  width: 120px;
}
.fields > div {
  margin: 5px;
}
<div class="fields">
  <div>
    <span>ItemID:</span>
    <span><input type="text" id="productId" placeholder="Item Id" /></span>
  </div>
  <div>
    <span>Description:</span>
    <span><input type="text" id="productDescription" placeholder="Product description" /></span>
  </div>
  <div>
    <span>Quantity:</span>
    <span><input type="number" min="1" id="productQty" placeholder="Quantity" /></span>
  </div>
  <div>
    <span>Price:</span>
    <span><input type="number" min="0" step="0.01" id="productPrice" placeholder="Price" /></span>
  </div>
  <button type="button" onclick="addProduct()">Add to product list</button>
</div>
<h1>Products</h1>
<div id="products">
</div>
<h1>Shopping cart</h1>
<div id="cart">
</div>

答案 1 :(得分:0)

已经快两年了...但是解决方案实际上很简单:

首先,尝试在控制台上吐出您的产品名称...该值包含空格! ! !

这意味着购物车无需进行任何处理即可添加原始值,因此匹配名称和价格的条件永远不会为真,因此会重复。

解决方案1(乏味):在添加到购物车之前修剪所有值的空格。

解决方案2(首选):不要在值和HTML标记之间添加任何空格!

"<strong class="item_price">price</strong>"

<!-- "NOT -->

"<strong class="item_price">
    price
</strong>"