jquery select custom attribute in child controls - javascript

I have a table with class="cloneable" that has rows containing one control per row. The controls may be textarea, checkbox, radio, or input[type=number]. Each control has a custom data-answersetid attribute. I would like to select all child controls under the .cloneable class and update each of the data-answersetid attributes to the value of a counter variable, lastanswersetid. I tried the following but it didn't work.
let lastanswersetid: number = 0;
const me: any = $('.cloneable').last(); //there might be several cloneables so just get the last cloneable
$('*[answersetid]', me) ? .each(function(index, value) {
console.log(index, value);
console.log($(this));
lastanswersetid++;
$(this).prop('aswersetid', lastAnswersetid);
}); //set the data-answersetid to the last answerset value
<table id="questionTable" class="table #("Q33,Q65".Contains(Model.QuestionnaireAndChoices.First().QuestionKey) ? "cloneable" : "")">
<tbody>
#foreach (var item in Model.QuestionnaireAndChoices)
{
<tr id="#item.QuestionID"
#("Q64".Contains(item.QuestionKey) ? "class=cloneable" : "Q70,Q71".Contains(item.QuestionKey) ? "class=notCloneable" : "")>
<td>
#Html.DisplayFor(modelItem => item.QuestionKey)
</td>
<td class="question">
#Html.DisplayFor(modelItem => item.Question)
</td>
<td>
#switch (item.ChoiceUIType)
{
case "UI-CHK":
#await Component.InvokeAsync("Checkbox2", new { userID = Model.UserID, questionID = item.QuestionID, questionKey = item.QuestionKey, choiceList = item.Choices });
break;
case "UI-DROP1":
#await Component.InvokeAsync("Dropdown2", new { userID = Model.UserID, questionID = item.QuestionID, questionKey = item.QuestionKey, choiceList = item.Choices });
break;
case "UI-RAD":
#await Component.InvokeAsync("RadioButton2", new { userID = Model.UserID, questionID = item.QuestionID, questionKey = item.QuestionKey, choiceList = item.Choices, question = item.Question, radioGroupName = item.QuestionKey });
break;
case "UI-NUMBER":
{
int thisLength = 10;
string thisUiWidth = "150px";
if (item.QuestionKey == "Q38")
{
thisLength = 3;
}
#await Component.InvokeAsync("Input", new { userID = Model.UserID, questionID = item.QuestionID, questionKey = item.QuestionKey, choiceList = item.Choices, inputLength = thisLength, uiWidth = thisUiWidth });
break;
}
case "UI-TXT":
{
<div class="md-form amber-textarea active-amber-textarea">
<textarea name="#item.QuestionKey" id="#item.QuestionKey" class="md-textarea form-control persistable" rows="3"
data-questionid="#item.QuestionID" data-userid="#Model.UserID"
data-questionkey="#item.QuestionKey"
data-useranswerid="#item.Choices.FirstOrDefault()?.UserAnswerID"
data-codesetid="#item.Choices.FirstOrDefault()?.ChoiceID"
data-oldvalue="#item.Choices.FirstOrDefault()?.OtherDescription"
data-answersetid="#item.Choices.FirstOrDefault()?.AnswerSetID"
data-toggle="tooltip" data-placement="left"
title="#(!string.IsNullOrEmpty(item.Choices.FirstOrDefault()?.OtherDescription) ? string.Format("Answered by {0}", item.Choices.First().AnsweredBy) : "")">#item.Choices.FirstOrDefault()?.OtherDescription</textarea>
</div>
break;
}
default:
{
#*just display the text*#
<div>Render raw text here</div>
break;
}
}
</td>
</tr>
}
</tbody>
</table>

Your selector is wrong, it should be [data-answersetid], also use .data() to set the value.
$('[data-answersetid]', me) ? .each(function(index, value) {
console.log(index, value);
console.log($(this));
lastanswersetid++;
$(this).data('answersetid', lastAnswersetid);
}); //set the data-answersetid to the last answerset value
If you need the actual attribute in the DOm updated you'd need to use .attr()
$(this).attr('data-answersetid', lastAnswersetid);

Related

How do I dynamically change the ID to do calculations?

I have the same form duplicated and triplicated, and depending on clicks on a button, they appear. What I want to do is to be able to make calculations, for example, of the cell Quantity and Unit_Price a multiplication and that the result is reflected in Total Price.
I need the Quantity and Unit_Price IDs to change dynamically so that the result is displayed per cell in their respective Total Price. I already wrote a piece of code that takes the number of each cell, the problem is that I don't know how to put it with the ID so that it is dynamic (the number should change depending on the cell it is in)
This is where I don't know how the code should be:
function multiplicar() {
let quantity = parseInt(document.getElementById('id__form-1-quantity').value);
let unit_price = document.getElementById('id__form-1-unit_price').value;
let total_price = document.getElementById('id__form-1-total_price');
total_price.value = quantity * unit_price;
}
function updateEmptyFormIDs(element, totalForms) {
var thisInput = element
var currentName = element.attr('name')
var newName = currentName.replace(/__prefix__/g, totalForms)
thisInput.attr('name', newName)
thisInput.attr('id', "id__" + newName)
var newFormRow = element.closest(".part-form");
var newRowId = "row_id_" + newName
newFormRow.attr("id", newRowId)
newFormRow.addClass("new-parent-row")
var parentDiv = element.parent();
parentDiv.attr("id", "parent_id_" + newName)
var inputLabel = parentDiv.find("label")
inputLabel.attr("for", "id_" + newName)
return newFormRow
}
function addForm() {
//$('.add-new-form').click(function(e){
var formId = "id_form-TOTAL_FORMS"
var emptyRow = $("#empty-row").clone();
emptyRow.attr("id", null);
var totalForms = parseInt($('#' + formId).val());
var newFormRow;
emptyRow.find("input,select,textarea").each(function() {
newFormRow = updateEmptyFormIDs($(this), totalForms)
})
$(".part-form:last").after(newFormRow)
$('#' + formId).val(totalForms + 1);
}
<table>
<tr>
<td id="parent_id_form-1-quantity">
<input type="number" name="form-1-quantity" class="form-control" id="id__form-1-quantity">
</td>
<td id="parent_id_form-1-unit_price">
<input type="number" name="form-1-unit_price" class="form-control" onchange="multiplicar()" id="id__form-1-unit_price">
</td>
<td id="parent_id_form-1-total_price">
<input type="number" name="form-1-total_price" class="form-control" id="id__form-1-total_price">
</td>
</tr>
</table>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>

Generated row of table event listener not working instantly

I have a table like this :
I have added an event listener assetTableEvent() to each text box in the table. My issue is when I add new row to the table, i also add the corresponding event listener to it assetTableEvent(), but the total value does not pop while entering value, it shows only when next row has values entered.
function assetTableEvent() {
let total = 0;
for (var k = 0; k < document.getElementById("assetTable").rows.length; k++) {
var a = document.getElementById("v" + k);
var o = document.getElementById("o" + k);
var t = document.getElementById("t" + k);
if (a == null || o == null) {
continue;
}
if (a.value.length > 0 && o.value.length > 0) {
t.value = Number.parseInt(a.value - o.value);
total = (Number.parseInt(t.value) + Number.parseInt(total));
document.getElementById("totalAssets").value = Number.parseInt(total);
}
}
}
I even tried calling assetTableEvent() every time there is a change, but it just does not work. Can somebody help me in Javascript how to make dynamically added rows correspond to event listener like above rows.
HTML for Asset table:
<div id="calcContainer">
<div id = "headingText" >
Child Maintenance Calculator
</div>
<div id="startPage">
<button id="startBtn">Start</button>
</div>
<div id="asset">
<table id="assetTable">
<tr>
<th>Asset</th>
<th>Value</th>
<th>Own</th>
<th>Total</th>
</tr>
</table>
<div id="totalAssetsDiv">
<Label for ="totalAssets">Total Assets</Label>
<input type="number" id = "totalAssets" readonly="true">
<br>
</div>
<div id ="rowOps">
<br> <button id="addRow" class = "addDel">Add Row</button>
<button id="removeRow" class = "addDel1">Delete Row</button><br>
</div>
</div>
And add row event listener :
document.getElementById("addRow").addEventListener("click", function () {
var table = document.getElementById("assetTable");
var row = table.insertRow();
for (let j = 0; j < 4; j++) {
var tb = document.createElement("INPUT");
var value = "", idNum = "";
if (j == 0) {
tb.setAttribute("type", "text");
tb.value = value;
}
else {
tb.setAttribute("type", "number");
}
//Setting textbox id
switch (j) {
case 0:
idNum = "a";
break;
case 1:
idNum = "v";
break;
case 2:
idNum = "o";
break;
case 3:
idNum = "t";
break;
}
tb.id = idNum + (table.rows.length);
if (tb.id.includes('t')) {
tb.setAttribute("readOnly", "true");
}
tb.classList.add("assetTBox");
let cell = row.insertCell(j);
tb.addEventListener("input", assetTableEvent, false);
cell.appendChild(tb);
}
});
Trying to use incremental IDs is more work than it is worth, especially when you start removing rows.
I suggest you use classes instead and delegate the event listener to the table itself. When an input event occurs you get the closest row and query for the elements within that row for the row total, then query all of the rows totals for the master total
Basic example with functional add row
const table = document.querySelector('#myTable'),
cloneRow = table.rows[0].cloneNode(true);
table.addEventListener('input',(e) => {
if (e.target.matches('.qty, .price')) {
const row = e.target.closest('tr'),
price = row.querySelector('.price').valueAsNumber || 0,
qty = row.querySelector('.qty').valueAsNumber || 0;
row.querySelector('.amt').value = qty * price;
setTotalAmt()
}
});
document.querySelector('#add-row').addEventListener('click', (e) => {
table.appendChild(cloneRow.cloneNode(true))
});
function setTotalAmt() {
const sum = [...table.querySelectorAll('.amt')].reduce((a, el) => a + (+el.value || 0), 0)
document.querySelector('#total').value = sum;
}
<button id="add-row">
Add Row
</button>
Total:<input id="total" />
<table id="myTable">
<tr>
<td>Qty:
<input type="number" class="qty" value="0" />
</td>
<td>Price:
<input type="number" class="price" value="0" />
</td>
<td>Amt:
<input class="amt" readonly value="0" />
</td>
</tr>
</table>
#charlietfl 's solition is more elegant but if you wonder what is the problem in your code, you should change the < to <= in k < document.getElementById("assetTable").rows.length; part
function assetTableEvent() {
let total = 0;
for (var k = 0; k <= document.getElementById("assetTable").rows.length; k++) {
var a = document.getElementById("v" + k);
var o = document.getElementById("o" + k);
var t = document.getElementById("t" + k);
if (a == null || o == null) {
continue;
}
if (a.value.length > 0 && o.value.length > 0) {
t.value = Number.parseInt(a.value - o.value);
total = (Number.parseInt(t.value) + Number.parseInt(total));
document.getElementById("totalAssets").value = Number.parseInt(total);
}
}
}
Here is the working example:
document.getElementById("addRow").addEventListener("click", function () {
var table = document.getElementById("assetTable");
var row = table.insertRow();
for (let j = 0; j < 4; j++) {
var tb = document.createElement("INPUT");
var value = "", idNum = "";
if (j == 0) {
tb.setAttribute("type", "text");
tb.value = value;
}
else {
tb.setAttribute("type", "number");
}
//Setting textbox id
switch (j) {
case 0:
idNum = "a";
break;
case 1:
idNum = "v";
break;
case 2:
idNum = "o";
break;
case 3:
idNum = "t";
break;
}
tb.id = idNum + (table.rows.length);
if (tb.id.includes('t')) {
tb.setAttribute("readOnly", "true");
}
tb.classList.add("assetTBox");
let cell = row.insertCell(j);
tb.addEventListener("input", assetTableEvent, false);
cell.appendChild(tb);
}
});
function assetTableEvent() {
let total = 0;
for (var k = 0; k <= document.getElementById("assetTable").rows.length; k++) {
var a = document.getElementById("v" + k);
var o = document.getElementById("o" + k);
var t = document.getElementById("t" + k);
if (a == null || o == null) {
continue;
}
if (a.value.length > 0 && o.value.length > 0) {
t.value = Number.parseInt(a.value - o.value);
total = (Number.parseInt(t.value) + Number.parseInt(total));
document.getElementById("totalAssets").value = Number.parseInt(total);
}
}
}
<div id="calcContainer">
<div id = "headingText" >
Child Maintenance Calculator
</div>
<div id="startPage">
<button id="startBtn">Start</button>
</div>
<div id="asset">
<table id="assetTable">
<tr>
<th>Asset</th>
<th>Value</th>
<th>Own</th>
<th>Total</th>
</tr>
</table>
<div id="totalAssetsDiv">
<Label for ="totalAssets">Total Assets</Label>
<input type="number" id = "totalAssets" readonly="true">
<br>
</div>
<div id ="rowOps">
<br> <button id="addRow" class = "addDel">Add Row</button>
<button id="removeRow" class = "addDel1">Delete Row</button><br>
</div>
</div>

Duplicate of Product in Shopping Cart

I have a simple problem in my shopping cart function. After i clicked the add to cart button, if it has the same product ID, it outputs a new product in a new row. It should just increase the product's quantity if it has the same product 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>
So currently your code is setup that you remove all the products when adding them to the product table, however you do not do the same when adding them to the cart. So just adding this would remove everything from your cart table
Array.from(cartsTable.children).slice(1).forEach(entry => cartsTable.removeChild(entry));
There are however some minor problems with your current code, nl:
If you add twice the same product id, you don't validate if the price or the description is the same
If you add products to the carts table, you only increase the quantity by 1, however, the product itself may have a higher quantity set, so you can fix it like this
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();
}
You could use <input type="number" /> for the product quantity
You could use <input type="number" step="0.01" /> for the price field
Upon removing an item from the products table, the product should no longer be available in the cart, so you should add a call to remove the product from the cart as well
You have 2 functions that create a table, both could be generalized to share the same functionality
I have rewritten the function you are describing a bit, using an answer I had already given on another question which will create a table based on given columns and an array containing the data
It still has the problem that it won't validate differences in the description / price when adding the same product, but it helps for all other problems I have mentioned.
It might be a bit longer code, however, that is partly due to the table helper function which can do quite some things and might be overkill for the scenario you describe. It does however work, and made it a lot easier to integrate the table creation ;)
// 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>
It's been almost 2 years... But the solution is simple actually:
First, try to spit out the name of your product on the console... the value has spaces ! ! !
This means that the cart is adding the raw values without any processing, therefore the condition to match the name and price will never be true, hence the duplicates.
Solution 1 (tedious): Trim spaces of all values prior to adding to the cart.
Solution 2 (preferred): Don't add any spaces between the values and the HTML tags!
"<strong class="item_price">price</strong>"
<!-- "NOT -->
"<strong class="item_price">
price
</strong>"

Getting a value from #Html.DisplayFor and #Html.HiddenFor

The HTML
int i = 1;
foreach (var item in Model.MyDataset)
{
<td class="tdBorder">
#Html.DisplayFor(x => item.ID, new { id = "VisibleID" + #i })
#Html.HiddenFor(x => item.ID, new { id = "HiddenID" + #i })
</td>
i += 1;
}
The jQuery
for (i = 1; i <= rowCount; i++) {
var myID_Visible = $.trim($("#VisibleID" + i).val());
var myID_Hidden = $.trim($("#HiddenID" + i).val());
}
I'm trying to learn some MVC and jQuery.
Would some one explain to me why calling
var myID_Visible = $.trim($("#VisibleID" + i).val()); returns an empty string but
var myID_Hidden = $.trim($("#HiddenID" + i).val()); returns the value of my item.ID?
The only difference is that the first jQuery line refers to a #Html.DisplayFor (returns empty string) while the second jQuery line refers to a #Html.HiddenFor (returns actual value).
Why can't i get a value from the #Html.DisplayFor?
Because #Html.DisplayFor() does not render a control and you cannot use .val(). Instead use
myID_Visible = $.trim($("#VisibleID" + i).text())
although this will depend on the html that #Html.DisplayFor() is rendering (are you using a display template?). You need to check the html generated.
By default DisplayFor will just render the text value of the property. You would need to do something like
int i = 1;
#foreach (var item in Model.MyDataset)
{
<td class="tdBorder">
<span id="#i">
#Html.DisplayFor(x => item.ID)
</span>
#Html.HiddenFor(x => item.ID, new { id = "HiddenID" + #i })
</td>
i += 1;
}
and in the script
myID_Visible = $.trim($('#' + i).text());
Some input has .html() rather than .val() try:
var myID_Visible = $.trim($("#VisibleID" + i).html());
EDIT
Another thing, remove the # before the i, you are already inside a C# code
#Html.DisplayFor(x => item.ID, new { id = "VisibleID" + i })
The reason is explained in this and that.
For your case however, I prefer to make it like this:
HTML
int i = 1;
foreach (var item in Model.MyDataset)
{
<td class="tdBorder">
<p id="VisibleID#(i)">#Html.DisplayFor(x => item.ID)</p>
<p id="HiddenID#(i)">#Html.HiddenFor(x => item.ID)</p>
</td>
i += 1;
}
So in the script we can call it:
for (i = 1; i <= rowCount; i++) {
var myID_Visible = $("#VisibleID" + i).text();
var myID_Hidden = $("#HiddenID" + i).text();
}
Hopefully this can help you, cheers!
#Html.DisplayFor(x => item.ID, new { id = "VisibleID" + #i })
rendred html looks like:
<span id="VisibleID1">item id value here</span>
whereas
#Html.HiddenFor(x => item.ID, new { id = "HiddenID" + #i })
rendered html looks like
<input type="hidden" id="HiddenID1" value="item id value here">
So in order to have display for value you should you use, $("#VisibleID1).html() as noted by #Shadi

Javascript: Validate radio buttons

I am trying to validate a group of two sex radio buttons, but am getting an error.
I have the following:
It is giving an error on itm.length; says it is undefined.
function validateSex(itm, elmt) {
var dom = document.getElementById(elmt);
var numChecked = 0;
for (var index = 0; index < itm.length; index++) {
if (itm[index].checked) {
numChecked++;
break;
}
}
// if sex not selected display error message.
if (numChecked === 0) {
dom.innerHTML = "Please select the sex.";
return false;
}else {
dom.innerHTML = "";
return true;
}
}
<table>
<tr>
<td>
<input type = "radio" name = "sex" value= "male"
title = "Select sex"
onchange = "validateSex(this, 'msgSex')"/>Male
<input type = "radio" name = "sex" value= "female"
title = "Select sex"
onchange = "validateSex(this, 'msgSex')"/>Female
</td>
<td id = "msgSex"></td>
</tr>
<table>
You should use document.getElementsByName('sex') instead of this in validateSex(this, 'msgSex') if you need the length attribute.
But you can't validate radio button like this.

Categories

Resources