0

I have a table with three columns in the header. I want to bind a function that calculates that third column--the running total column. When I added this binding to the thead in the template, I got the results I wanted. But I lost my table header. I worked in a solution proposed by a user, and it worked, my table head came back. But now the problem is, there is another view using the expanding table template. What I need to do is, in this ng-repeat property in $table.properties track by property.type, I need to check to see whether the table has the 'runningTotal' type property. And if it does, to fire my calculateRunningTotal function. Otherwise this function is firing for every table using this template and trying to manipulate the third column, whatever that may be. That's not what I need.

<th ng-bind="calculateRunningTotal(property.type)" ng-repeat="property in $table.properties track by property.type"
            class="col-xs-{{property.size}} clickable" ng-click="$table.sorter.changeType(property.type)">
            {{property.label}} &nbsp;
          <i class="fa fa-caret-{{$table.sorter.directionFor(property.type)}} fa-lg"></i>
        </th>

Store Tender Totals Now Missing First Two Column Headers Access Logs Table Now Missing All Column Headers

This is the template code:

import app from 'act/app';
import * as table from 'act/components/table';
import './expanding-table.styles.less';
import * as $ from 'jquery';

const expander = `
  <td class="col-xs-1 text-center link" ng-click="$event.stopPropagation()">
    <i class="fa fa-chevron-{{$item.expanded ? 'up' : 'down'}}" ng-click="$item.expanded = !$item.expanded"></i>
  </td>
`;

const fakeRows = `
  <tbody ng-if="!$table.list.length">
    <tr class="dummy-data" ng-repeat="dummy in $table.fakeRows">
      ${expander}

      <td ng-repeat="property in $table.properties track by property.type">
        dummy
      </td>
    </tr>
  </tbody>
`;

const header = `
  <thead>
    <th>

    </th>

    <th ng-bind="calculateRunningTotal()" ng-repeat="property in $table.properties track by property.type"
        class="col-xs-{{property.size}} clickable" ng-click="$table.sorter.changeType(property.type)">
        {{property.label}} &nbsp;
      <i class="fa fa-caret-{{$table.sorter.directionFor(property.type)}} fa-lg"></i>
    </th>
  </thead>
`;

const rows = ({ row, ngRepeat, cell }) => `
  <tbody ng-if="$table.list.length">
    <tr class="clickable"
        ng-repeat-start="${ngRepeat}"
        ng-click="$item.expanded = !$item.expanded"
        ng-init="$item.expanded = false">
      ${row({ cell })}
    </tr>

    <tr class="expanded-row" ng-show="$item.expanded"
        ng-repeat-end>
      <td colspan="12">
        <div expanded-placeholder></div>
      </td>
    </tr>
  </tbody>
`;

const row = ({ cell }) => `
  ${expander}

  ${cell}
`;

app.directive('actExpandingTable', ['$compile', $compile =>
  table.defaultDirective({
    link: function($scope, $element, $attrs, $tables, $transclude) {
      // wondering what's going on here? see table/table.resources.js

      const $el = $(table.generateTemplate({ header, rows, row, fakeRows }));
      $scope.vm = $scope.$parent.vm;
      $scope.calculateRunningTotal= function(){
 if(type ==="runningTotal"){
        console.log("Calculate Running Total Called");
        var attendantTotalCell = 0;
        var total = 0;
        var totalCell="";
        var totalsCells = document.querySelectorAll("td:nth-child(3)");

        totalsCells.forEach(function(cell, index){
          totalCell = cell.innerText;
          attendantTotalCell = totalCell.replace(/[^0-9-.]/g, '');
          total = parseInt(attendantTotalCell) + parseInt(total);
          if(cell.nextElementSibling){
          cell.nextElementSibling.innerHTML = '$' + total.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');
          }
        })

        var tableHeadRow = document.querySelectorAll('th.col-xs-2.clickable');
        tableHeadRow.forEach(th =>{
          th.addEventListener("click", function(){
            console.log("Table head clicked");
            var attendantTotalCell = 0;
            var total = 0;
            var totalCell="";
            totalsCells.forEach(function(cell, index){
              totalCell = cell.innerText;
              attendantTotalCell = totalCell.replace(/[^0-9-.]/g, '');
              total = parseInt(attendantTotalCell) + parseInt(total);
              if(cell.nextElementSibling){
              cell.nextElementSibling.innerHTML = '$' + total.toFixed(2).replace(/\d(?=(\d{3})+\.)/g, '$&,');
              }
            })
          })
        }) 
         return label;
         }
        }
      $transclude($scope, content => {
        if (content.length > 1) {
          $el.find('[expanded-placeholder]').replaceWith(content);
        } else {
          throw new Error('Expanding table requires transcluded content to show when expanded!');
        }

        $compile($el)($scope, v => $element.append(v));
      });
    },
  })
]);

1 Answers1

1

Use double curly markup {{calculateRunningTotal()}} instead of ng-bind ,as ng-bind with a function, will not be fired for every digest cycle unless you are passing any variable to that method. Check this link for more details - AngularJS ng-bind with a function

<th ng-repeat="property in $table.properties track by property.type"
        class="col-xs-{{property.size}} clickable" ng-click="$table.sorter.changeType(property.type)">
        {{calculateRunningTotal()}}{{property.label}} &nbsp;
      <i class="fa fa-caret-{{$table.sorter.directionFor(property.type)}} fa-lg"></i>
    </th>

Reason for empty header with example :

th tag displays return value value of ng-bind function, as calculateRunningTotal calculates total and no return value, header is getting as empty

To resolve your issue with ng-bind, pass property.label to ng-bind="calculateRunningTotal(property.label)" and return value from calculateRunningTotal

$scope.calculateRunningTotal= function(label){
  //total calculation here

return label

}

codepen sample for reference - https://codepen.io/nagasai/pen/vzqadB

Naga Sai A
  • 10,771
  • 1
  • 21
  • 40
  • I'm going to edit my original question because what you've suggested works now using what you suggested above. But there is another view using this expanding-table template. The column header using property 'type' is now broken and displaying NAN. I'll update my question to be more clear. –  Sep 25 '18 at 19:05
  • I've updated my question. Thanks for sticking with me here and seeing this through. –  Sep 25 '18 at 19:17
  • try passing property.type calculateRunningTotal(property.type) and inside function check that type $scope.calculateRunningTotal= function(type){ if(type ==="runningTotal"){ //execute calculation} } – Naga Sai A Sep 25 '18 at 19:23
  • I think we're getting closer. I've updated my question to show what the tables look like now. With the last suggestion basically what I want to happen is happening. But I'm missing table head columns in both tables now. But I think we're close.. –  Sep 25 '18 at 19:45
  • i think you forget add return value for $scope.calculateRunningTotal= function(label){ //total calculation here return label } – Naga Sai A Sep 25 '18 at 19:49
  • ng-bind="calculateRunningTotal(property.label)" – Naga Sai A Sep 25 '18 at 19:49
  • ng-bind will bind the returned label, so that the header will have the label name and it will not be empty – Naga Sai A Sep 25 '18 at 19:50
  • in your case to handle all scenarios, pass label and type , as calculateRunningTotal(property.label , property.type) – Naga Sai A Sep 25 '18 at 19:52
  • Yes sir. It works now. I am grateful. Many thanks. –  Sep 25 '18 at 20:00
  • glad it works as expected :) ..thanks @CMazz for confirming – Naga Sai A Sep 25 '18 at 20:01