0

Using JavaScript I am returning a list of store attendants and dynamically inserting a row after the last row (attendant) displaying the total for all attendants under the total column. But now what I need to do is to display a running total, for each attendant, in the running total column.

Here's what I'm displaying, clearly the running totals formula isn't right here...

enter image description here

I know there is a more Angular-y way to do this. And I will implement that way for sure. But for now, I'd just like you to focus on the path I'm heading down, regarding calculating this running total colum. And here specifically is what I'd like you to focus on:

var total = 0;
  document.querySelectorAll("td:nth-child(4)").forEach((cell, index) => {
    this.attendantNames.forEach(a=>{
      this.total += a.total;
    });
    cell.innerText = this.total;
  });

But if you need to see the whole controller code, it's right here:

export default class StoreTotalsController {
  constructor() {
    this.attendantNames = [];
    this.stores = [];
    this.emptyResult = true;
    this.totals = 0;
    this.total = 0;
    this.cellValue = "";
    this.runningTotalCells;
    //this.previousTotal = 0;
  }

  getAttendants() {
    showLoader('Searching');
    const baseUrl = '/src/areas/store-totals/services/tender-total-data.json';
    const getStores = new Request(baseUrl, {
      method: 'GET'
      });
    fetch(getStores).then(function(response){
      return response.json();
    }).then(resp => {
    if (!(resp[0] && resp[0].error)) {
      this.attendantNames = resp.stores[0].attendants;
      this.attendantNames.forEach(a=>{
        this.totals += a.total;
        console.log("Attendant :" , a.attendantName, "Had a total of :" , a.total, "With running total " , this.totals);
      });

      //Row at bottom displaying total of totals
      var table = document.querySelector('.table');
      var td1 = document.createElement('td');
      var td2 = document.createElement('td');
      var td3 = document.createElement('td');
      var tr = document.createElement("tr");
      tr.setAttribute("class", "last-row");
      var td1Data = document.createTextNode('  ');
      var td2Data = document.createTextNode('Total');
      var td3Data = document.createTextNode('$' + this.totals.toFixed(2));
      td1.appendChild(td1Data);
      td2.appendChild(td2Data);
      td3.appendChild(td3Data);
      tr.appendChild(td1);
      tr.appendChild(td2);
      tr.appendChild(td3);
      table.appendChild(tr);

      this.emptyResult = false;
      this.errorMessage = null;

    } else {
      this.errorMessage = resp[0].error.name;
    }
    digest();

  var total = 0;
  document.querySelectorAll("td:nth-child(4)").forEach((cell, index) => {
    this.attendantNames.forEach(a=>{
      this.total += a.total;
    });
    cell.innerText = this.total;
  });
    showLoader(false);
    });
  }

  searchIfReady() {
    if (this.search && this.date && this.date.isValid()) {
      this.getSearch();
    }
  }

  updateDate(date) {
    this.date = moment(date).startOf('day');
    this.searchIfReady();
  }
}
StoreTotalsController.$inject = ['$stateParams'];

2 Answers 2

1
let total = 0;

document.addEventListener("DOMContentLoaded", function(event) { 
  document.querySelectorAll("td:nth-child(2)").forEach((cell, index) => {
    const cellInnerHTML = cell.innerHTML
    const numericTotal = cellInnerHTML.substring(1, cellInnerHTML.length);

    total += parseInt(numericTotal) * 1000;

    cell.parentElement.querySelectorAll("td")[2].innerHTML = total;
  });
});

Codepen:

https://codepen.io/anon/pen/vzQyGy

  1. you need to take care of removing $ sign.
  2. after parseInt, because of format of your numbers, you will be left with (according to your example) 2, 8 and 5, instead of 2000, 8000, 5000. I handle it naively by multiplying by 1000. Better solution should be found, this is just a sketch.
  3. You need to format numbers back to what you want.

Solution is there, I leave details to you :)

In case of your code it should go like this:

this.attendantNames.forEach((attendant, index) => {
    const table = document.querySelector('.table');
    const attendantRow = table.querySelectorAll('tr')[index];
    const runningTotalCell = attendantRow.querySelectorAll('td')[2];

    //am assuming that this.totals is 0 at the beginning of this loop, right?
    this.totals += parseInt(attendant.total); //am not sure of format attendant.total have in your data, not sure if you need parsing here and if  it will work

    runningTotalCell.innerHTML = `$${this.totals.toFixed(2)}`; 
})

It should work, but not being able to run the code, see if it actually does or throws errors, debug it etc. I can't give you 100%. In case of any error please try to also solve it yourself :) If you will get stuck, come back and ask telling me what is wrong and where, I will try to help again :)

General Comments

  • your method is named getAttendants and it does WAAAAY more than that (getting attendants). It fetches attendants data, it creates parts of the table and it fills that table with data. It should not. Method should do only one thing (Single Responsibility Principle) and be named properly.
  • you should split responsibilities between few functions and pass data between them, allowing each function to handle just one task

Ex. - function A - fetches attendants data - function B - creates the table - function C - fills that table with attendants data

Inside of those functions you can split your code into smaller functions. Where you place comments, you may use functions that will have commented code inside and just by name, they will explain developer what they do. Ex.

Three lines below are used in each loop, only to get access to that runnintTotalCell.

const table = document.querySelector('.table');
const attendantRow = table.querySelectorAll('tr')[index];
const runningTotalCell = attendantRow.querySelectorAll('td')[2];

You can extract them and wrap with separate function:

getRunningTotalCellForRow(rowIndex) { 
    const table = document.querySelector('.table');
    const attendantRow = table.querySelectorAll('tr')[index];
    return attendantRow.querySelectorAll('td')[2];
}

And use in forEach loop.

Etc.

Generally to learn proper way of coding you can take some tutorials, read some books and get to know the rules :)

As for books - Clean Code by Robert C. Martin is one of canonical ones. As for tutorials and other online resources.

Search in browser of your choosing texts like: - how to write clean code - single responsibility principle - DRY KISS SOLID - key software development principles

I think from websites you will find, you can find much much more and get into it.

Good luck and have fun!

After you edited your code:

I checked your screenshot. The red part is telling me, that in first row you insert TOTAL FOR ALL (around 32k) in second cell it s that value, but doubled, in third value times three. That leads to conclusion, that you must be messing some additions in your loop.

So

var total = 0;
document.querySelectorAll("td:nth-child(4)").forEach((cell, index) => {
  this.attendantNames.forEach(a=>{
    this.total += a.total;
  });
  cell.innerText = this.total;
});

What you are doing above:

  1. you defined var total = 0 (and never use it)

  2. for each 4th cell

  3. take all attendants and for each attendant

  4. add its total value to this.total

  5. and insert it into innerText

Do you see now? For each cell you are inserting summarized totals for all attendants (plus this.totals value you already have, as you never zero it).

Proper version below:

const runningTotalCells = document.querySelectorAll("td:nth-child(4)");
//no need for nested loops
this.attendantNames.forEach((attendant, index) => {
  this.total += a.total;
  runningTotalCells[index].innerText = this.total;
})

Does it work now?

Sign up to request clarification or add additional context in comments.

10 Comments

could you place that function within the context of my function? I mean edit your answer to include the whole getAttendants() function. I'd appreciate it.
@CMazz please try now. I only coped forEach loop, it should replace your loop
thank you. I've edited my post. Maybe you can take a look if you have the time. Again thanks.
I am thanking you again for your comments. I appreciate the insight you've passed.
@CMazz check last part and if I've helped you, please mark it as answer :). All the parts are in this post, you just need to be careful and precise :) good luck
|
0

You can loop each second cell with $("td:nth-child(2)").each then add that cells value to the total variable and print out the total to the next td with $(this).next('td').text(total);

var total = 0;
$("td:nth-child(2)").each(function( index ) {
  total = parseInt($(this).text()) + parseInt(total);
  $(this).next('td').text(total);
});
table{
text-align: center;
}
td{
width: 100px;
background-color: orange;
}
th{
background-color: orange;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<table>
<th>name</th>
<th>Total</th>
<th>Running Total</th>
<tr>
<td>Eric</td>
<td>100</td>
<td></td>
</tr>

<tr>
<td>Lars</td>
<td>20</td>
<td></td>
</tr>

<tr>
<td>Patrick</td>
<td>200</td>
<td></td>
</tr>
</table>

1 Comment

your answer is beautiful and works obviously. But I need to use pure JavaScript.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.