I am not sure how to create custom headers based on each grouped table.
Currently the table groups together but it just show 1 row with a plus sign you can barely see.
What I want to try and do is replace the row information to show how many items are in the row with a header name of the first td item.
Example of what I have a what I am trying to do
//Making timesheet <td>
fetch(timehsheetUrl date)
.then(response => response.json())
.then(data => makeTable(data.contracts))
.catch(error => console.log(`Oh no! ${error}`))
const makeTable = (contracts) => {
console.log(contracts);
const body = document.getElementById('tsdata');
contracts.forEach((person, index) => {
person.details.forEach((entry) => {
const dateString = entry.timesheetDate;
const dateObject = new Date(dateString);
const dd = dateObject.getDate();
const mm = dateObject.toLocaleString('en-us', {
month: 'short'
});
const yyyy = dateObject.getFullYear();
const chartDate = `${dd}-${mm}-${yyyy}`
console.log(chartDate);
const htmlTemplate = `
<tr class = "testDeleteTimesheet">
<td>${entry.contractCode}</td>
<td style="display:none;">${entry.timesheetDetailsId}</td>
<td>${entry.activityCode}</td>
<td>${entry.otFlag}</td>
<td>${entry.notes}</td>
<td>${chartDate}</td>
<td class = "totalHours">${entry.hours}</td>
<td><button id=\"edit-" counter "\" class=\"btn editRow btnStyle btn-primary btn-sm\"><span class=\ "bi bi-pencil\"></span></button>
<button id=\"delete-" counter "\" class=\"btn delRow btnStyle btn-danger btn-sm\"><span class=\"bi bi-eraser\"></span></button></td>
</tr>
`;
body.innerHTML = htmlTemplate;
});
//Making timesheet easier to read
const tables = $('table')[0];
const rowGroups = {};
//loop through the rows excluding the first row (the header row)
while(tables.rows.length > 1){
const row = tables.rows[1];
const id = $(row.cells[0]).text();
if(!rowGroups[id]) rowGroups[id] = [];
if(rowGroups[id].length > 0){
row.className = 'subrow';
$(row).slideUp();
}
rowGroups[id].push(row);
tables.deleteRow(1);
}
//loop through the row groups to build the new table content
for(let id in rowGroups){
const group = rowGroups[id];
for(let j = 0; j < group.length; j ){
const row = group[j];
if(group.length > 1 && j == 0) {
//add button
const lastCell = row.cells[row.cells.length - 1];
$("<span class='collapsed'>").appendTo(lastCell).click(plusClick);
}
tables.tBodies[0].appendChild(row);
}
}
//function handling button click
function plusClick(e){
const collapsed = $(this).hasClass('collapsed');
const fontSize = collapsed ? 14 : 0;
$(this).closest('tr').nextUntil(':not(.subrow)').slideToggle(400)
.css('font-size', fontSize);
$(this).toggleClass('collapsed');
}
});
};
Sample of what my api looks like:
"contracts":
[
{
"contractID": "Test1",
"details": [
{
"timesheetDetailsId": 111111,
"timesheetId": 0,
"timesheetDate": "2021-11-01T00:00:00",
"timesheetDayNumber": 1,
"contractCode": "Test1",
"activityCode": "GRA",
"hours": 7.5,
"otFlag": false,
"notes": "Testing",
"approved": false,
"overUnder": 0.0,
"employeeCode": "N-0510"
},
{
"timesheetDetailsId": 111113,
"timesheetId": 0,
"timesheetDate": "2021-11-03T00:00:00",
"timesheetDayNumber": 3,
"contractCode": "Test1",
"activityCode": "GRA",
"hours": 7.5,
"otFlag": false,
"notes": "Testing",
"approved": false,
"overUnder": 0.0,
"employeeCode": "N-0510"
}
]
"contractID": "Test2",
"details": [
{
"timesheetDetailsId": 111112,
"timesheetId": 0,
"timesheetDate": "2021-11-02T00:00:00",
"timesheetDayNumber": 2,
"contractCode": "Test2",
"activityCode": "GRA",
"hours": 7.5,
"otFlag": false,
"notes": "Testing",
"approved": false,
"overUnder": 0.0,
"employeeCode": "N-0510"
}
]
]
CodePudding user response:
Lets tidy this up a bit.
You can do this in one pass, you know how many items are in a group by the number of elements in the array. You can also wrap each "group" in a tbody
element.
Next we will use a document fragment so that we only update the DOM once whic will reduce redraws, important if there is a lot of items.
Then we wil use event delegation to handle the click event on dynamically added elements. I've alos used a data attribute instead of a hidden cell.
//Making timesheet <td>
/* Mocked some datat to emualte call
fetch(timehsheetUrl date)
.then(response => response.json())
.then(data => makeTable(data.contracts))
.catch(error => console.log(`Oh no! ${error}`))
*/
const makeTable = (contracts) => {
const body = document.getElementById('tsdata');
//Create a fragment to work with
var fragment = new DocumentFragment();
contracts.contracts.forEach((person, index) => {
/*Create a group header row for each "person"*/
let groupRow = document.createElement("tr");
groupRow.classList.add("group_row");
groupRow.innerHTML = `<td colspan="8">${person.contractID} : Found ${person.details.length}</td>`
/*Create a tbody to hold each group*/
let groupBody = document.createElement("tbody");
groupBody.classList.add("group_body");
/*Append to fragment*/
fragment.appendChild(groupRow);
fragment.appendChild(groupBody);
person.details.forEach((entry) => {
const dateString = entry.timesheetDate;
const dateObject = new Date(dateString);
const dd = dateObject.getDate();
const mm = dateObject.toLocaleString('en-us', {
month: 'short'
});
const yyyy = dateObject.getFullYear();
const chartDate = `${dd}-${mm}-${yyyy}`
const htmlTemplate = `
<tr class = "testDeleteTimesheet" data-timesheetdetailsid="${entry.timesheetDetailsId}">
<td>${entry.contractCode}</td>
<td>${entry.activityCode}</td>
<td>${entry.otFlag}</td>
<td>${entry.notes}</td>
<td>${chartDate}</td>
<td class = "totalHours">${entry.hours}</td>
<td><button id=\"edit-" counter "\" class=\"btn editRow btnStyle btn-primary btn-sm\"><span class=\ "bi bi-pencil\"></span></button>
<button id=\"delete-" counter "\" class=\"btn delRow btnStyle btn-danger btn-sm\"><span class=\"bi bi-eraser\"></span></button></td>
</tr>
`;
/*Update the group body*/
groupBody.innerHTML = htmlTemplate;
});
});
//Append the fragment to the table
body.appendChild(fragment);
};
/*Event listener to toggle expantion, bound to table so we can use dynamically added element*/
document.getElementById("tsdata").addEventListener("click", function(event){
/*Check the target of the event*/
if(event.target.matches(".group_row>td")){
//Toggle the class on the row and let CSS do the rest
event.target.closest(".group_row").classList.toggle("expanded");
}
});
let data = {
"contracts": [{
"contractID": "Test1",
"details": [{
"timesheetDetailsId": 111111,
"timesheetId": 0,
"timesheetDate": "2021-11-01T00:00:00",
"timesheetDayNumber": 1,
"contractCode": "Test1",
"activityCode": "GRA",
"hours": 7.5,
"otFlag": false,
"notes": "Testing",
"approved": false,
"overUnder": 0.0,
"employeeCode": "N-0510"
},
{
"timesheetDetailsId": 111113,
"timesheetId": 0,
"timesheetDate": "2021-11-03T00:00:00",
"timesheetDayNumber": 3,
"contractCode": "Test1",
"activityCode": "GRA",
"hours": 7.5,
"otFlag": false,
"notes": "Testing",
"approved": false,
"overUnder": 0.0,
"employeeCode": "N-0510"
}
]
}, {
"contractID": "Test2",
"details": [{
"timesheetDetailsId": 111112,
"timesheetId": 0,
"timesheetDate": "2021-11-02T00:00:00",
"timesheetDayNumber": 2,
"contractCode": "Test2",
"activityCode": "GRA",
"hours": 7.5,
"otFlag": false,
"notes": "Testing",
"approved": false,
"overUnder": 0.0,
"employeeCode": "N-0510"
}]
}]
};
makeTable(data);
table{width:90%;}
/*Hide TBody By Default*/
.group_row:not(.expanded) tbody {height:0; display:none;}
/*Set the cell to relative to position /-*/
.group_row > td {position:relative;}
/*Expand Collapes indicator*/
.group_row > td::after {
position:absolute;
right: 5px;
content: " ";
}
/*Change indicator*/
.group_row.expanded > td::after {content:"-";}
<table id="tsdata">
<thead>
<tr>
<th>Contract Code</th>
<th>Activity Code</th>
<th>Overtime</th>
<th>Notes</th>
<th>Date</th>
<th>Total Hours</th>
<th> </th>
<th> </th>
</tr>
</thead>
</table>
<iframe name="sif1" sandbox="allow-forms allow-modals allow-scripts" frameborder="0"></iframe>