How to recursively sum parent child hierarchy value in jquery table - javascript

I have parent child hierarchy data loaded from database into html table structure where i need to enter value in some of leaf node.
When I enter value in any leaf node then recursively do sum in hierarchy up to last parent node.
I need to do this using jquery/javascript and html table tree format.
This is my html
//This is textbox change event.
$(document).on('change', '.item-balance', function () {
if (!$.isNumeric($(this).val())) return false;
calculatehierarchybalance($(this).closest('tr'));
});
//Below is calculation function. Here problem is that it only sum up to single level instead of all the parent of leaf node.
var calculatehierarchybalance = function (currentrow) {
var inpctrl = $(currentrow).find(".item-balance");
var _total = 0;
$.each(currentrow, function () {
var ss = $('tr[data-id=' + $(currentrow).attr('data-parentid') + ']');
//console.log($(ss).attr('data-id'), $(ss).attr('data-parentid'));
if (inpctrl.is('input:text')) {
_total = _total + Number(inpctrl.val());
}
else {
_total = _total + Number($(ss).find('td.td-bal').text().trim());
}
$(ss).find('td[data-readonly=true]').text(_total);
if ($.isNumeric($(ss).attr('data-parentid')))
calculatehierarchybalance($(ss));
});
};
.table {
width: 100%;
max-width: 100%;
margin-bottom: 1rem;
background-color: transparent;
}
.table > thead > tr > th, .table > tbody > tr > th, .table > tfoot > tr > th, .table > thead > tr > td, .table > tbody > tr > td, .table > tfoot > tr > td {
border-top: 1px solid #e7eaec;
border-bottom: 1px solid #e7eaec;
line-height: 1.42857;
padding: 8px;
vertical-align: top;
}
.table-Balance td {
border: 1px solid #eee;
}
.collapse-tr .toggle {
background: url("https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-ios7-minus-empty-128.png");
}
.expand-tr .toggle {
background: url("https://cdn4.iconfinder.com/data/icons/ionicons/512/icon-ios7-plus-empty-128.png");
}
.toggle {
height: 9px;
width: 9px;
display: inline-block;
}
.panel-title {
color: #fff !important;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table class="table table-balance" id="tblbl">
<thead>
<tr>
<th>Catálogo de Cuentas</th>
<th class="text-right">Balance</th>
</tr>
</thead>
<tbody>
<tr data-depth="0" class="collapse-tr level0" data-id="81" data-parentid="" data-readonly="true"
data-baseaccounttype="1" data-accounttype="1" data-accountid="2">
<td style="padding-left:0px;" class="align-middle"><span class="toggle collapse-tr"></span>1-Activos</td>
<td class="text-right td-bal" data-readonly="true">
0.00 </td>
</tr>
<tr data-depth="1" class=" level1" data-id="82" data-parentid="81" data-readonly="true"
data-baseaccounttype="1" data-accounttype="1" data-accountid="3">
<td style="padding-left:12px;" class="align-middle"><span class=" "></span>1.1-Activos Corrientes</td>
<td class="text-right td-bal" data-readonly="true">
0.00 </td>
</tr>
<tr data-depth="2" class=" level2" data-id="83" data-parentid="82" data-readonly="true"
data-baseaccounttype="1" data-accounttype="2" data-accountid="4">
<td style="padding-left:18px;" class="align-middle"><span class=" "></span>1.1.10-Efectivo y Equivalentes</td>
<td class="text-right td-bal" data-readonly="true">
0.00 </td>
</tr>
<tr data-depth="3" class=" level3" data-id="84" data-parentid="83" data-readonly="true"
data-baseaccounttype="1" data-accounttype="2" data-accountid="5">
<td style="padding-left:24px;" class="align-middle"><span class=" "></span>1.1.10.1-Cajas</td>
<td class="text-right td-bal" data-readonly="true">
0.00 </td>
</tr>
<tr data-depth="4" class=" level4" data-id="85" data-parentid="84" data-readonly="false"
data-baseaccounttype="1" data-accounttype="2" data-accountid="6">
<td style="padding-left:30px;" class="align-middle"><span class=" "></span>1.1.10.1.10-Caja General</td>
<td class="text-right td-bal" data-readonly="false">
<input class="form-control text-right input-sm inputdecimal item-balance" id="Balance" name="Balance" placeholder="Ingresar saldo" type="text" value="0.00" /> </td>
</tr>
<tr data-depth="3" class=" level3" data-id="87" data-parentid="83" data-readonly="true"
data-baseaccounttype="1" data-accounttype="2" data-accountid="8">
<td style="padding-left:24px;" class="align-middle"><span class=" "></span>1.1.10.2-Bancos</td>
<td class="text-right td-bal" data-readonly="true">
0.00 </td>
</tr>
<tr data-depth="4" class=" level4" data-id="88" data-parentid="87" data-readonly="false"
data-baseaccounttype="1" data-accounttype="2" data-accountid="9">
<td style="padding-left:30px;" class="align-middle"><span class=" "></span>1.1.10.2.10-Banco #1 (Cambiar Nombre)</td>
<td class="text-right td-bal" data-readonly="false">
<input class="form-control text-right input-sm inputdecimal item-balance" id="Balance" name="Balance" placeholder="Ingresar saldo" type="text" value="0.00" /> </td>
</tr>
<tr data-depth="4" class=" level4" data-id="86" data-parentid="84" data-readonly="false"
data-baseaccounttype="1" data-accounttype="2" data-accountid="7">
<td style="padding-left:12px;" class="align-middle"><span class=" "></span>1.2-Depósitos sin aplicar</td>
<td class="text-right td-bal">0.00
</td>
</tr>
<tr data-depth="4" class=" level4" data-id="86" data-parentid="84" data-readonly="false"
data-baseaccounttype="1" data-accounttype="2" data-accountid="7">
<td style="padding-left:18px;" class="align-middle"><span class=" "></span>1.2.1-Depósitos sin aplicar 1</td>
<td class="text-right td-bal">
<input class="form-control text-right input-sm inputdecimal item-balance" id="Balance" name="Balance" placeholder="Ingresar saldo" type="text" value="0.00" /> </td>
</tr>
</tbody>
</table>
Expected Output as below image when I entered value in leaf node that will reflect to all related parent node.
Any help would be appreciated.
Thank you.

A bit late for an answer, but I enjoyed giving an answer :)
I chose a bit different approach:
create an object that holds the data recursively
make the value "reactive" by using getters
I guess neither too efficient nor very scalable, but solves the problem in a logically simple way.
// sum the value of immediate children's values
function getRecursiveVal(items) {
return items.length ? items.reduce((a, c) => a + c.val, 0) : 0
}
// recursive object getter
// gets an item by label
function findByLabel(label, item) {
if (item.label === label) {
return item
} else if (item?.length || item?.items?.length) {
let ret
for (let i = 0; i < item.items.length; i++) {
ret = findByLabel(label, item.items[i])
if (ret) {
break;
}
}
return ret
}
}
// array to hold the data
// if readonly is false, then the item's
// beginning val is 0
// if readonly is true, the the items's
// beginning val is the sum of its items vals
const arr = [
{
label: "1-Activos",
get val() { return getRecursiveVal(this.items) },
readonly: true,
items: [
{
label: "1.1-Activos Corrientes",
get val() { return getRecursiveVal(this.items) },
readonly: true,
items: [
{
label: "1.1.10-Efectivo y Equivalentes",
get val() { return getRecursiveVal(this.items) },
readonly: true,
items: [
{
label: "1.1.10.1-Cajas",
get val() { return getRecursiveVal(this.items) },
readonly: true,
items: [
{
label: "1.1.10.1.10-Caja General",
val: 0,
readonly: false,
items: [],
},
],
},
{
label: "1.1.10.2-Bancos",
get val() { return getRecursiveVal(this.items) },
readonly: true,
items: [
{
label: "1.1.10.2.10-Banco #1 (Cambiar Nombre)",
val: 0,
readonly: false,
items: [],
},
],
}
],
}
],
},
{
label: "1.2-Depósitos sin aplicar",
get val() { return getRecursiveVal(this.items) },
readonly: true,
items: [
{
label: "1.2.1-Depósitos sin aplicar 1",
val: 0,
readonly: false,
items: [],
},
],
}
]
},
];
// row template
function rowHtml(data) {
let html = '<tr>'
html += `<td>${ data.label }</td>`
if (data.readonly) {
html += `<td>${ data.val }</td>`
} else {
html += `
<td>
<input
data-inputfor="${ data.label }"
value="${ Number(data.val) }"
/>
</td>
`
}
html += '</tr>'
return html
}
// flattening the object for
// HTML table
function recTableHtml(item) {
let html = rowHtml(item)
if (item?.items?.length) {
html += item.items.map(e => recTableHtml(e)).join("")
}
return html
}
const tbody = document.getElementById("tbody");
function updateTable(container, arr, fn) {
container.innerHTML = arr.map(e => fn(e)).join("")
}
// first paint of the table
(function(container) {
updateTable(tbody, arr, recTableHtml)
})(tbody);
jQuery(document).ready(function($) {
// general action on input
$("body").on("input", "input", function(e) {
const thisLabel = this.getAttribute("data-inputfor")
const item = findByLabel(thisLabel, arr[0])
item.val = Number($(this).val())
updateTable(tbody, arr, recTableHtml)
// setting focus to last char of input
// so there's continuous typing
var value = $(`[data-inputfor="${thisLabel}"]`).val();
$(`[data-inputfor="${thisLabel}"]`).focus().val('').val(value)
})
})
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table>
<tbody id="tbody"></tbody>
</table>

Related

HTML/JS output table not showing horizontal borders

I can't figure out why my horizontal borders are not showing up with my output section. See code and screenshot below:
I would like to have horizontal borders and if possible keep the date fields from
wrapping into the next row below itself.
I would like to have horizontal borders and if possible keep the date fields from wrapping into the next row below itself.
<td align="center"><input type="button" value="Submit" name="submit" id="submit" onClick="display()" /></button></td>
</tr>
</table>
<table width="400px" align="center" colspan="40" table border="5">
<tr style="background-color:#8FBC8F;">
<td align="center"><b>Name</b></td>
<td align="center"><b>Company</b></td>
<td align="center"><b>Time In</b></td>
<td align="center"><b>Time Out</b></td>
<td align="center"><b>Description of Work</b></td>
</tr>
<tr>
<td align="center"><div id="displayarea"></div></td>
<td align="center"><div id="displayarea1"></div></td>
<td align="center"><div id="displayarea2"></div></td>
<td align="center"><div id="displayarea3"></div></td>
<td align="center"><div id="displayarea4"></div></td>
</tr>
I would like to have horizontal borders and if possible keep the date fields from wrapping into the next row below itself.
function getValue() {
var Items = "";
var td1 = document.getElementById("displayarea").innerHTML.split("<br>");
var td2 = document.getElementById("displayarea1").innerHTML.split("<br>");
var td3 = document.getElementById("displayarea2").innerHTML.split("<br>");
var td4 = document.getElementById("displayarea3").innerHTML.split("<br>");
var td5 = document.getElementById("displayarea4").innerHTML.split("<br>");
for (var i = 0; i < td1.length; i++) {
if (td1[i])
Items += td1[i] + " ,";
if (td2[i])
Items += td2[i] + " ,";
if (td2[i])
Items += td2[i] + " ,";
if (td3[i])
Items += td3[i] + " ,";
if (td4[i])
Items += td4[i] + " ";
Items += "\n";
}
console.log(Items);
return Items;
}
function display() {
document.getElementById("displayarea").innerHTML += document.getElementById("fname").value + "<br />";
document.getElementById("fname").value = "";
document.getElementById("displayarea1").innerHTML += document.getElementById("lname").value + "<br />";
document.getElementById("lname").value = "";
document.getElementById("displayarea2").innerHTML += document.getElementById("sname").value + "<br />";
document.getElementById("sname").value = "";
document.getElementById("displayarea3").innerHTML += document.getElementById("pname").value + "<br />";
document.getElementById("pname").value = "";
document.getElementById("displayarea4").innerHTML += document.getElementById("jname").value + "<br />";
document.getElementById("jname").value = "";
}
I highly suggest you start separating your data from your presentation of that data.
So, split your display function into two: createRow and renderRows. Likewise, getValues can just be getRows.
Note that this required a different way of doing things in your code, so I also refactored your HTML and CSS to bring it more in line with modern methods.
function getRows(data) {
return data.map(datum => Object.values(datum).join(',')).join('\n');
}
function createRow(data) {
const datum = {
fname: document.getElementById("fname").value,
lname: document.getElementById("lname").value,
sname: new Date(document.getElementById("sname").valueAsNumber).toLocaleString(),
pname: new Date(document.getElementById("pname").valueAsNumber).toLocaleString(),
jname: document.getElementById("jname").value
};
data.push(datum);
document.getElementById("dataForm").reset();
renderRows(data);
}
function renderRows(data) {
const body = document.getElementById("renderedData");
body.innerHTML = "";
for (let datum of data) {
let tr = document.createElement('tr');
let tdFName = document.createElement('td');
tdFName.appendChild(document.createTextNode(datum.fname));
tr.appendChild(tdFName);
let tdLName = document.createElement('td');
tdLName.appendChild(document.createTextNode(datum.lname));
tr.appendChild(tdLName);
let tdSName = document.createElement('td');
tdSName.appendChild(document.createTextNode(datum.sname));
tr.appendChild(tdSName);
let tdPName = document.createElement('td');
tdPName.appendChild(document.createTextNode(datum.pname));
tr.appendChild(tdPName);
let tdJName = document.createElement('td');
tdJName.appendChild(document.createTextNode(datum.jname));
tr.appendChild(tdJName);
body.appendChild(tr);
}
}
window.addEventListener('load', () => {
const data = [];
document.getElementById('add').addEventListener('click', function(e) {
createRow(data);
});
document.getElementById('getData').addEventListener('click', function(e) {
console.log(getRows(data));
});
});
form {
width: max-content;
margin: 0 auto 1rem;
}
.control-group {
display: flex;
justify-content: space-between;
}
fieldset {
display: flex;
flex-flow: column nowrap;
}
fieldset button {
align-self: flex-end;
}
<form id="dataForm">
<fieldset>
<legend>Enter Data</legend>
<div class="control-group">
<label for="fname">Name:</label>
<input id="fname" type="text">
</div>
<div class="control-group">
<label for="lname">Company:</label>
<input id="lname" type="text">
</div>
<div class="control-group">
<label for="sname">Time In:</label>
<input id="sname" type="datetime-local">
</div>
<div class="control-group">
<label for="pname">Time Out:</label>
<input id="pname" type="datetime-local">
</div>
<div class="control-group">
<label for="jname">Description of Work:</label>
<textarea id="jname"></textarea>
</div>
<button type="button" id="add">Add</button>
</fieldset>
</form>
<table width="400px" align="center" colspan="40" table border="5">
<thead>
<tr style="background-color:#8FBC8F;" id='header'>
<td align="center"><b>Name</b></td>
<td align="center"><b>Company</b></td>
<td align="center"><b>Time In</b></td>
<td align="center"><b>Time Out</b></td>
<td align="center"><b>Description of Work</b></td>
</tr>
</thead>
<tbody id="renderedData">
</tbody>
</table>
<button type="button" id="getData">Get Data</button>
To get borders for all cells, add this at the top of your html code (inside the head):
<style>
table {
border-collapse: collapse;
}
td {
border: 1px solid #555;
}
</style>
Adjust the border thickness, style and color as you like (in the border setting of td)
Here's an easy way to add a row with each addition using the element - available in all good browsers (not so fast Internet Explorer, I wasn't talking to you)
I've also changed how to read the values
note, using class instead of id in the cells in the rows - to make it easy to get them all with minimal change to your code
Although, personally I'd get the row data data differently (shown in alternativeGetValues)
function getValue() {
var Items = "";
var td1 = [...document.querySelectorAll(".displayarea")].map(e => e.innerHTML);
var td2 = [...document.querySelectorAll(".displayarea1")].map(e => e.innerHTML);
var td3 = [...document.querySelectorAll(".displayarea2")].map(e => e.innerHTML);
var td4 = [...document.querySelectorAll(".displayarea3")].map(e => e.innerHTML);
var td5 = [...document.querySelectorAll(".displayarea4")].map(e => e.innerHTML);
for (var i = 0; i < td1.length; i++) {
if (td1[i])
Items += td1[i] + " ,";
if (td2[i])
Items += td2[i] + " ,";
if (td3[i])
Items += td3[i] + " ,";
if (td4[i])
Items += td4[i] + " ,";
if (td5[i])
Items += td5[i] + " ";
Items += "\n";
}
console.log(Items);
return Items;
}
function display() {
const template = document.getElementById("row");
const clone = template.content.cloneNode(true);
const additem = (dest, src) => {
const s = document.querySelector(src);
clone.querySelector(dest).innerHTML = s.value;
s.value = "";
};
additem(".displayarea", "#fname");
additem(".displayarea1", "#lname");
additem(".displayarea2", "#sname");
additem(".displayarea3", "#pname");
additem(".displayarea4", "#jname");
template.insertAdjacentElement('beforebegin', clone.firstElementChild);
}
// IMHO this is better
function alternateGetValue() {
const Items = [...document.querySelectorAll('.data')]
.map(row => [...row.querySelectorAll('td>div')]
.map(d => d.textContent).join(',')
).join('\n');
console.log(Items);
return Items;
}
.wide {
min-width:12em;
}
F: <input id="fname"> <br>
L: <input id="lname"> <br>
S: <input id="sname"> <br>
P: <input id="pname"> <br>
J: <input id="jname"> <br>
<input type="button" value="add" onclick="display()"/>
<input type="button" value="show" onclick="getValue()"/>
<input type="button" value="Better" onclick="alternateGetValue()"/>
<table width="400px" align="center" colspan="40" table border="5">
<thead>
<tr style="background-color:#8FBC8F;" id='header'>
<td align="center"><b>Name</b></td>
<td align="center"><b>Company</b></td>
<td align="center" class="wide"><b>Time In</b></td>
<td align="center" class="wide"><b>Time Out</b></td>
<td align="center"><b>Description of Work</b></td>
</tr>
</thead>
<tbody>
<template id="row">
<tr style="background-color:#8F8FBC;" class="data">
<td align="center"><div class="displayarea"></div></td>
<td align="center"><div class="displayarea1"></div></td>
<td align="center"><div class="displayarea2"></div></td>
<td align="center"><div class="displayarea3"></div></td>
<td align="center"><div class="displayarea4"></div></td>
</tr>
</template>
</tbody>
</table>

Cannot read property 'childNodes' checkbox to get value from same row

Below code should get me the value of the column next to the checked box in the table, but, once the button is clicked, I am getting:
Cannot read property childNodes of null
Note: database from firebase which where the values from the table come from
Table image :
rootRefReAgents.on("child_added", snap => {
var AgentName = snap.child("Name").val();
$("#table_body_Test").append("<tr><td>" + AgentName + "</td><td><INPUT TYPE=\"Checkbox\"> </Input></td></tr>");
});
}
function ActionData(){
let agents = [];
let table = document.getElementById("table_body_Test");
let childNodes = Array.from(table.childNodes);
// let childNodes = table.childNodes;
for (let child of childNodes.values()) {
console.log(`child: ${child}`);
if (child.constructor.name !== "HTMLTableRowElement") {
continue;
}
let agent = child.childNodes.item(1).innerHTML;
console.log(`agent: ${agent}`);
let checkbox = child.childNodes.item(3).childNodes.item(1);
console.log(`checkbox: ${checkbox}`);
console.log(checkbox.checked);
if (checkbox.checked) {
agents.push(agent);
}
}
console.log(`agents: ${agents}`);
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.3.1/jquery.min.js"></script>
<table id="testTable" align="center">
<thead>
<tr style="color: #D2002E; background: #FFCC01; height:32px;">
<td>Agents</td>
<td>Select</td>
</tr>
</thead>
<tbody id="table_body_Test">
</tbody>
</table>
<button id="submitBtn" onclick="ActionData()">Next</button>
ES6 brought new methods that simplify the code and its reading
const tableBody = document.querySelector('#testTable tbody' );
document.querySelector('#submitBtn').onclick=()=>
{
let agents = [];
console.clear();
for (let rowX of tableBody.rows )
{
let
agent = rowX.cells[0].textContent,
checkbox = rowX.cells[1].querySelector('input[type=checkbox]')
;
console.log( 'agent:', agent, 'checked:', checkbox.checked);
if (checkbox.checked) { agents.push(agent); }
}
console.log( 'agents (array):', agents.join(' / '));
}
/// bonus info :
/*
rootRefReAgents.on("child_added", snap=>{
let
newRow = tableBody.insertRow(-1);
newRow.insertCell(0).textContent = snap.child("Name").val();
newRow.insertCell(1).innerHTML = '<input type="Checkbox">';
});
*/
#testTable { margin: auto; border-collapse: collapse }
#testTable thead tr {
color: #D2002E;
background: #FFCC01;
height:32px;
font-weight: bold;
}
#testTable tr:nth-child(even) {background-color: lightgrey }
#testTable td { border:1px solid grey; padding: 0 20px; }
#testTable td:nth-child(2) { text-align: center }
<table id="testTable">
<thead>
<tr> <td>Agents</td> <td>Select</td> </tr>
</thead>
<tbody>
<tr> <td>AMC</td> <td> <input type="checkbox" > </td> </tr>
<tr> <td>Mygulfex</td> <td> <input type="checkbox" > </td> </tr>
<tr> <td>topStar</td> <td> <input type="checkbox" > </td> </tr>
<tr> <td>WMC</td> <td> <input type="checkbox" > </td> </tr>
</tbody>
</table>
<button id="submitBtn" >see Selects in console</button>

jquery DataTables parent and child rows posted to server as one record instead of two

I have a problem where jquery DataTables creates parent and child rows on resize (responsive DataTable) and I need to save values of inputs from child rows as well as from parent rows and post via ajax to controller action.
Responsive (resized) DataTable:
Normal (not resized) DataTable:
Currently I am using this jquery function to post data to the server:
$('#SaveItemButton').click(function (e) {
var arr = [];
var rows = $('#ItemTable').find('tbody').find('tr');
console.log(rows.length);
$.each(rows, function (index, item) {
var controls = $(this).find('input, select');
console.log(controls.length);
item = {
ItemType: controls.eq(0).val(),
Unit: controls.eq(1).val(),
Quantity: controls.eq(2).val(),
Price: controls.eq(3).val(),
InvoiceDate: $('#InvoiceDate').val(),
TransferDate: $('#TransferDate').val(),
TransferPlace: $('#TransferPlace').val(),
InvoiceDescription: $('#InvoiceDescription').val()
};
arr.push(item);
});
$.ajax({
url: '/Item/Add',
data: JSON.stringify(arr),
contentType: 'application/json',
type: "POST",
dataType: "json",
success: function (result) {
//alert(result);
},
error: function (errormessage) {
}
});
return false;
});
but when the Datatable is resized it returns two rows which in turn gets posted to the server.
I am retrieving rows from a table via:
var rows = $('#ItemTable').find('tbody').find('tr');
How can I get all the related parent rows and child rows as one row so I can post that row to the server?
Parent row example:
<tr role="row" class="odd parent">
<td tabindex="0" style=""></td>
<td class="sorting_1"><input name="ItemType" class="form-control" type="text"></td>
<td style="display: none;"><select name="Unit" class="form-control defaultpicker"><option>dan</option><option>Komad</option><option>Sat</option>m<option>m2</option><option>m3</option><option>kg</option><option>lit</option><option>pak</option><option>reč</option></select></td>
<td style="display: none;"><input name="Quantity" class="form-control" type="number"></td>
<td style="display: none;"><input name="Price" class="form-control" type="text"></td>
<td style="display: none;"><input name="Total" class="form-control" type="text" readonly=""></td>
<td style="display: none;"><button type="submit" id="DeleteButton" class="fa fa-times select-row btn btn-secondary btn-sm" data-id=""></button>
</td>
</tr>
Child row example:
<tr class="child">
<td class="child" colspan="2">
<ul data-dtr-index="0" class="dtr-details">
<li data-dtr-index="2" data-dt-row="0" data-dt-column="2">
<span class="dtr-title">Unit</span>
<span class="dtr-data">
<select name="Unit" class="form-control defaultpicker"><option>dan</option><option>Komad</option><option>Sat</option>m<option>m2</option><option>m3</option><option>kg</option><option>lit</option><option>pak</option><option>reč</option></select>
</span>
</li>
<li data-dtr-index="3" data-dt-row="0" data-dt-column="3">
<span class="dtr-title">Quantity</span>
<span class="dtr-data">
<input name="Quantity" class="form-control" type="number" value="3">
</span>
</li>
<li data-dtr-index="4" data-dt-row="0" data-dt-column="4">
<span class="dtr-title">Price</span>
<span class="dtr-data">
<input name="Price" class="form-control" type="text" value="1000">
</span>
</li>
<li data-dtr-index="5" data-dt-row="0" data-dt-column="5">
<span class="dtr-title">Total</span>
<span class="dtr-data">
<input name="Total" class="form-control" type="text" readonly="" value="">
</span>
</li>
<li data-dtr-index="6" data-dt-row="0" data-dt-column="6">
<span class="dtr-title"></span>
<span class="dtr-data">
<button type="submit" id="DeleteButton" class="fa fa-times select-row btn btn-secondary btn-sm" data-id=""></button>
</span>
</li>
</ul>
</td>
</tr>
Controller posted data, index 0 contains valid data:
Code Snippet:
var table = $('#ItemTable').DataTable({
"dom": '<"toolbar">frtip',
"paging": true,
"pagingType": "full_numbers",
"searching": false,
// Solution to responsive table losing data
'columnDefs': [{
'targets': [1, 2, 3, 4, 5, 6],
'render': function(data, type, row, meta) {
if (type === 'display') {
var api = new $.fn.dataTable.Api(meta.settings);
var $el = $('input, select, textarea', api.cell({
row: meta.row,
column: meta.col
}).node());
var $html = $(data).wrap('<div/>').parent();
if ($el.prop('tagName') === 'INPUT') {
$('input', $html).attr('value', $el.val());
if ($el.prop('checked')) {
$('input', $html).attr('checked', 'checked');
}
} else if ($el.prop('tagName') === 'TEXTAREA') {
$('textarea', $html).html($el.val());
} else if ($el.prop('tagName') === 'SELECT') {
$('option:selected', $html).removeAttr('selected');
$('option', $html).filter(function() {
return ($(this).attr('value') === $el.val());
}).attr('selected', 'selected');
}
data = $html.html();
}
return data;
}
}],
'responsive': true,
order: [1, 'asc']
});
// Solution to responsive table losing data
$('#ItemTable tbody').on('keyup change', '.child input, .child select, .child textarea', function(e) {
var $el = $(this);
var rowIdx = $el.closest('ul').data('dtr-index');
var colIdx = $el.closest('li').data('dtr-index');
var cell = table.cell({
row: rowIdx,
column: colIdx
}).node();
$('input, select, textarea', cell).val($el.val());
if ($el.is(':checked')) {
$('input', cell).prop('checked', true);
} else {
$('input', cell).removeProp('checked');
}
});
$('#SaveItemButton').click(function() {
var arr = [];
var rows = $('#ItemTable').find('tbody').find('tr');
console.log(rows.length);
$.each(rows, function(index, item) {
var controls = $(this).find('input, select');
console.log(controls.length);
item = {
ItemType: controls.eq(0).val(),
Unit: controls.eq(1).val(),
Quantity: controls.eq(2).val(),
Price: controls.eq(3).val(),
InvoiceDate: $('#InvoiceDate').val(),
TransferDate: $('#TransferDate').val(),
TransferPlace: $('#TransferPlace').val(),
InvoiceDescription: $('#InvoiceDescription').val()
};
arr.push(item);
});
$.ajax({
url: '/Item/Add',
data: JSON.stringify(arr),
contentType: 'application/json',
type: "POST",
dataType: "json",
success: function(result) {
//alert(result);
},
error: function(errormessage) {
}
});
return false;
});
<link href="https://cdn.datatables.net/1.10.16/css/jquery.dataTables.min.css" rel="stylesheet" />
<link href="https://cdn.datatables.net/responsive/2.2.3/css/responsive.dataTables.min.css" rel="stylesheet" />
<table id="ItemTable" class="table table-hover table-secondary dataTable no-footer dtr-inline" style="width: 100%;" role="grid" aria-describedby="ItemTable_info">
<thead>
<tr role="row">
<th></th>
<th>ItemType</th>
<th>Unit</th>
<th>Quantity</th>
<th>Price</th>
<th>Total</th>
<th></th>
</tr>
</thead>
<tbody>
<tr role="row" class="odd parent">
<td tabindex="0" style=""></td>
<td class="sorting_1"><input name="ItemType" class="form-control" type="text"></td>
<td style="">
<select name="Unit" class="form-control defaultpicker">
<option>value1</option>
<option>value2</option>
<option>value3</option>
<option>value4</option>
<option>value5</option>
<option>value6</option>
<option>value7</option>
<option>value8</option>
<option>value9</option>
</select>
</td>
<td style=""><input name="Quantity" class="form-control" type="number"></td>
<td style=""><input name="Price" class="form-control" type="text"></td>
<td style=""><input name="Total" class="form-control" type="text" readonly=""></td>
<td style=""><button type="submit" id="DeleteButton" data-id=""></button></td>
</tr>
</tbody>
</table>
<script src="https://code.jquery.com/jquery-3.3.1.min.js" integrity="sha256-FgpCb/KJQlLNfOu91ta32o/NMZxltwRo8QtmkMRdAu8=" crossorigin="anonymous"></script>
<script src="https://cdn.datatables.net/1.10.16/js/jquery.dataTables.min.js"></script>
<script src="https://cdn.datatables.net/buttons/1.5.2/js/dataTables.buttons.min.js"></script>
<script src="https://cdn.datatables.net/select/1.2.6/js/dataTables.select.min.js"></script>
<script src="https://cdn.datatables.net/responsive/2.2.3/js/dataTables.responsive.min.js"></script>
Well, you really cannot. At first DT inject and remove child rows and their content to and from the DOM, making them invisible to simple jQuery selectors. You can target open child rows, but that is all.
Secondly you cannot select multiple elements in pairs. You could have for example $('tr.parent, tr.parent ~ tr.child') or similar, but that would be equal to just $('tr'). I would go through the API:
table.rows().every(function() {
var $node = this.nodes().to$();
var item = {
ItemType: $node.find('input[name=ItemType]').val(),
Unit: $node.find('select[name=Unit]').val(),
Quantity: $node.find('input[name=Quantity]').val(),
Price: $node.find('input[name=Price]').val(),
Total: $node.find('input[name=Total]').val(),
InvoiceDate: $('#InvoiceDate').val(),
TransferDate: $('#TransferDate').val(),
TransferPlace: $('#TransferPlace').val(),
InvoiceDescription: $('#InvoiceDescription').val()
};
arr.push(item)
})
Completely untested. See JQuery Datatables search within input and select on how to update the DT internals when form controls is changing. Otherwise you will just get return default / original values.

Comparing two values with dynamic rows using javascript

I have an html code with multiple rows containing two values.Using javascript,I am able to compare the two values for only one row.How can I do the same for dynamically generated rows.
HTML:
<tbody id="appendrow" class="appendrow">
<input type="text"
data-field="quantity"
required
class="form-control"
name="units_ordered[]"
id="units_ordered0"
value=""/>
<input type="text"
data-field="inventory"
class="form-control"
name="inventory"
id="inventory0"
value="" />
</tbody>
Javascript:
var counter = 1;
$(window).load(function () {
$(function () {
$('#addRow').click(function () {
var row = $(' <tbody id=\'appendrow\' class=\'appendrow\'><tr><td> <input type=\'text\' required class=\'form-control\' name=\'sku[]\' onclick=\'filtersku(this,' + counter + ');\'> <input type=\'hidden\' id=\'product_id' + counter + '\' name=\'product_id[]\'/></td> <td> <input type=\'text\' readonly data-field=\'vendorsku\' id=\'vendor_sku' + counter + '\' name=\'vendorsku[]\'class=\'form-control\'></td> <td><input class=\'form-control\' data-field=\'quantity\' type=\'text\' required id=units_ordered' + counter + '\' name=\'units_ordered[]\' /></td> <td><input class=\'form-control\' type=\'text\' data-field=\'price\' required name=\'vendor_unit_price[]\' id=\'vendor_price' + counter + '\'></td> <td><input name=\'discount_percent[]\' data-field=\'discount\' class=\'form-control\' ><input type=\'hidden\' data-field=\'discountnumber\' class=\'form-control\' id=\'discountnumber\' ></td><td><input type=\'text\' readonly data-field=\'tax\' id=\'tax_id' + counter + '\' class=\'form-control\' name=\'tax_id[]\' /></td><td> Rs. <span data-field=\'taxPrice\' id=\'taxPrice' + counter + '\' >0</span><input type=\'hidden\' data-field=\'taxPriceInput\' id=\'taxPriceInput' + counter + '\' class=\'taxPrice\' value = \'0\'/> </td> <td><input type=\'text\' class=\'form-control\'data-field=\'total\' name=\'subtotal[]\'/><input type=\'hidden\' data-field=\'totalbeforetax\' class=\'form-control\' id=\'subtotalbeforetax\' /></td><td><button type=\'button\' class=\'btn btn-danger\' name=\'delete[]\' onclick=\'javascript:deletefun(this);\'><i class=\'fa fa-trash\' aria-hidden=\'true\'></i></button></td></tr></tbody>');
$('#example').append(row);
counter++;
});
});
});
function validation() {
var ordered = parseInt(document.getElementById('units_ordered0').value);
var inventory = parseInt(document.getElementById('inventory0').value);
if (inventory == -1) {
return true;
}
else {
if (ordered <= inventory) {
return true;
}
else {
document.getElementById('error').innerHTML = ('Ordered units of a product cannot be greater than the number of products present in inventory whose value is ' + inventory).fontcolor('red');
return false;
}
}
}
Please note the correct structure of the HTML5 Table. Here is a clean HTML5 Table structure:
<table>
<caption>Table Caption</caption>
<colgroup>
<col>
<col>
</colgroup>
<thead>
<tr>
<th>Header Row 1</th>
<th>Header Row 2</th>
<th>Header Row 3</th>
</tr>
</thead>
<tfoot>
<tr>
<td>Footer Row 1</td>
<td>Footer Row 2</td>
<td>Footer Row 3</td>
</tr>
</tfoot>
<tbody>
<tr>
<td>Body Row 1</td>
<td>Body Row 2</td>
<td>Body Row 3</td>
</tr>
</tbody>
</table>
And about your question:
var counter = 1;
document.getElementById( 'addRow' ).addEventListener( 'click', function ( e ) {
var table = document.getElementById( 'myTable' ),
row = table.insertRow( counter ),
cell1 = row.insertCell( 0 ),
cell2 = row.insertCell( 1 );
cell1.innerHTML = '<input type="text" data-field="quantity" class="form-control" name="units_ordered[]" id="units_ordered' + counter + '" required/>';
cell2.innerHTML = '<input type="text" data-field="inventory" class="form-control" name="inventory" id="inventory' + counter + '"/>';
counter++;
e.preventDefault()
} )
document.getElementById( 'validate' ).addEventListener( 'click', function ( e ) {
validation();
e.preventDefault()
} )
function validation() {
var ordered = document.querySelectorAll( 'input[id^="units_ordered"]' ),
inventory = document.querySelectorAll( 'input[id^="inventory"]' );
document.getElementById( 'error' ).innerHTML = ''
ordered.forEach( function ( item, index ) {
var ordered_val = Number( item.value ) || 0,
inventory_val = Number( inventory[ index ].value ) || 0
if ( inventory_val == -1 ) {
return true
} else {
if ( ordered_val <= inventory_val ) {
return true
} else {
document.getElementById( 'error' ).innerHTML += 'Row ' + +( index + 1 ) + ': Ordered units of a product cannot be greater than the number of products present in inventory whose value is ' + inventory_val + '<br/>';
return false
}
}
} )
}
table {
font-family: arial, sans-serif;
border-collapse: collapse;
width: 300px
}
td, th {
border: 1px solid #86af49;
text-align: left;
padding: 0
}
th {
padding: 5px;
color: #fff;
background-color: #86af49
}
input[type="text"] {
box-sizing: border-box;
width: 100%;
border: 0;
outline: none
}
#error {
color: red;
font-size: .75em
}
<p>
<button type="button" id="addRow">Add Row</button>
<button type="button" id="validate">Validation</button>
</p>
<table id="myTable">
<thead>
<tr>
<th>Ordered</th>
<th>Inventory</th>
</tr>
</thead>
<tbody>
<tr>
<td>
<input type="text" data-field="quantity" class="form-control" name="units_ordered[]" id="units_ordered0" required/>
</td>
<td>
<input type="text" data-field="inventory" class="form-control" name="inventory" id="inventory0"/>
</td>
</tr>
</tbody>
</table>
<p id="error"></p>

Javascript battleship game hit detection not working

I'm trying to write code to detect if a ship has been hit or not. I have 3 ships on the board and each takes up 3 cells. Using js I placed the 3 ships on the board. And when I run the fire method, only the last two ships show they've been hit, but the first ship at location 00,01,02 doesn't indicate that it's been hit, even once. Where did I go wrong?
var view = {
showMessage: function(msg) {
var message = document.getElementById('message');
message.innerHTML = msg;
},
showHit: function(location) {
var cell = document.getElementById(location);
cell.setAttribute('class', 'hit');
},
showMiss: function(location) {
var cell = document.getElementById(location);
cell.setAttribute('class', 'miss');
}
}
var model = {
boardSize: 7,
numShips: 3,
shipsSunk: 3,
ships: [{
location: [00, 01, 02],
hits: ['', '', '']
},
{
location: [10, 11, 12],
hits: ['', '', '']
},
{
location: [20, 21, 22],
hits: ['', '', '']
}
],
fire: function(guess) {
for (var i = 0; i < this.numShips; i++) {
var ship = this.ships[i];
var index = ship.location.indexOf(guess);
if (index >= 0) {
ship.hits[index] = 'hit';
view.showHit(guess);
}
}
}
};
model.fire(10);
model.fire(11);
model.fire(12);
model.fire(20);
model.fire(21);
model.fire(22);
/*
model.fire(00);
model.fire(01);
model.fire(02); */
* {
margin: 0px;
padding: 0px;
}
body {
background-color: grey;
}
#message {
color: green;
font-size: 2em;
text-transform: uppercase;
font-family: sans-serif;
}
#board {
background: url('board.jpg') no-repeat;
width: 863px;
height: 1024px;
margin: auto;
position: relative;
}
table {
position: absolute;
left: 173px;
top: 98px;
}
td {
height: 94px;
width: 94px;
}
form input {
position: absolute;
right: 0px;
bottom: 0px;
background-color: green;
}
.hit {
background: url('ship.png') no-repeat center center;
}
.miss {
background: url('miss.png') no-repeat center center;
}
<div id='board'>
<div id='message'></div>
<table>
<tr>
<td id='00'></td>
<td id='01'></td>
<td id='02'></td>
<td id='03'></td>
<td id='04'></td>
<td id='05'></td>
<td id='06'></td>
</tr>
<tr>
<td id='10'></td>
<td id='11'></td>
<td id='12'></td>
<td id='13'></td>
<td id='14'></td>
<td id='15'></td>
<td id='16'></td>
</tr>
<tr>
<td id='20'></td>
<td id='21'></td>
<td id='22'></td>
<td id='23'></td>
<td id='24'></td>
<td id='25'></td>
<td id='26'></td>
</tr>
<tr>
<td id='30'></td>
<td id='31'></td>
<td id='32'></td>
<td id='33'></td>
<td id='34'></td>
<td id='35'></td>
<td id='36'></td>
</tr>
<tr>
<td id='40'></td>
<td id='41'></td>
<td id='42'></td>
<td id='43'></td>
<td id='44'></td>
<td id='45'></td>
<td id='46'></td>
</tr>
<tr>
<td id='50'></td>
<td id='51'></td>
<td id='52'></td>
<td id='53'></td>
<td id='54'></td>
<td id='55'></td>
<td id='56'></td>
</tr>
<tr>
<td id='60'></td>
<td id='61'></td>
<td id='62'></td>
<td id='63'></td>
<td id='64'></td>
<td id='65'></td>
<td id='66'></td>
</tr>
</table>
<form action='#' method='get'>
<input type='text' id='guessInput' placeholder='enter location: A0' />
<input type='button' name='submit' value='Fire!' name='fire' />
</form>
</div>
Your coordinates 00, 01, and 02 are interpreted as ints and are therefore resolved to 0, 1, and 2, because it is assumed that the leading zero is not needed for an int value. You can fix this by using strings to represent and compare the coordinates. Instead of model.fire(00), you will need model.fire("00"). Then to compare the input coordinates, you will need to compare guess.charAt(0) and guess.charAt(1) to determine if that coordinate is a hit.

Categories