Home > other >  How do I add a new <th> based on grouped rows?
How do I add a new <th> based on grouped rows?

Time:11-12

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

What I currently have

What I would like it 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>

  • Related