0

I have a register that lists people alphabetically from A to Z... Each person, Mr A for example, has a set of corresponding data Histories.

I have a table with a simple ng-repeat that displays the data for 12 cells (representing 12months).

If 12 months/cells worth of data is supplied, i show all the data, if only 5 months of data (anything less than 12) is provided, i call a service srvEmptyCells that calculates the remaining cells and displays in a darker colour.

The problem with this is, that i notice i am repeating the ng-repeat:

emptyCell in getEmptyCells

many many times which is impacting page performance given i have over 100 users.

Is there a way i save the number of empty cells for each particular user? And remove the need for the extra ng-repeats? Would a directive improve things?

Heres a plunker: http://plnkr.co/edit/2UKDD1fvfYGMjX9oJqVu?p=preview

HTML:

<table ng-repeat="data in myData" class="my-table">
  <caption>
    {{ data.Name }}
  </caption>
  <tbody>
    <tr>
      <th>Rate</th>
      <td ng-repeat="history in data.Histories.slice(0, 12)" class="my-table-cell">
        {{history.Rate}}
      </td>
      <td ng-repeat="emptyCell in getEmptyCells(data.Histories.slice(0, 12).length)" class="empty"></td>
    </tr>
    <tr>
      <th>Effort</th>
      <td ng-repeat="history in data.Histories.slice(0, 12)" class="my-table-cell">
        {{history.Effort}}
      </td>
      <td ng-repeat="emptyCell in getEmptyCells(data.Histories.slice(0, 12).length)" class="empty"></td>
    </tr>
    <tr>
      <th>Advance</th>
      <td ng-repeat="history in data.Histories.slice(0, 12)" class="my-table-cell">
        {{history.Advance}}
      </td>
      <td ng-repeat="emptyCell in getEmptyCells(data.Histories.slice(0, 12).length)" class="empty"></td>
    </tr>
    <tr>
      <th>Previous</th>
      <td ng-repeat="history in data.Histories.slice(0, 12)" class="my-table-cell">
        {{history.Previous}}
      </td>
      <td ng-repeat="emptyCell in getEmptyCells(data.Histories.slice(0, 12).length)" class="empty"></td>
    </tr>
    <tr>
      <th>Current</th>
      <td ng-repeat="history in data.Histories.slice(0, 12)" class="my-table-cell">
        {{history.Current}}
      </td>
      <td ng-repeat="emptyCell in getEmptyCells(data.Histories.slice(0, 12).length)" class="empty"></td>
    </tr>
    <tr>
      <th>Code</th>
      <td ng-repeat="history in data.Histories.slice(0, 12)" class="my-table-cell">
        {{history.Code}}
      </td>
      <td ng-repeat="emptyCell in getEmptyCells(data.Histories.slice(0, 12).length)" class="empty"></td>
    </tr>
  </tbody>
</table>

JS:

app.controller('MainCtrl', function($scope, $http, factoryGetJSONFile, srvEmptyCells) {
  $scope.name = 'World';

  factoryGetJSONFile.getMyData(function(data) {
    $scope.myData = data.MyData.Entries;
  });

  $scope.getEmptyCells = srvEmptyCells.getEmptyCells;

});

app.factory('srvEmptyCells', function() {
    return {
        getEmptyCells: function(len) {
            var emptyCells = [];
            for(var i = 0; i < 12 - len; i++){
                emptyCells.push(i);
            }
            return emptyCells;
        }
     };
 });
Oam Psy
  • 8,555
  • 32
  • 93
  • 157

2 Answers2

1

You could modify $scope.myData by adding a cached version of data.Histories.slice(0, 12) and getEmptyCells(data.Histories.slice(0, 12).length) and using ng-repeat on those instead. This way you'd save repeating those calls over and over again.

A better way, if you can modify the response, would be to do this service side rather than on the client. Why to return more than 12 histories if you won't need them?

It is hard to know if this will fix your performance issue, as there are many ng-repeats per row, but it will be faster than before at least.

Sample on caching client side;

factoryGetJSONFile.getMyData(function(data) {
   $scope.myData = data.MyData.Entries;

   for (int i=0; i < $scope.myData.length; i++) {
     var current = $scope.myData[i];
     current.nonEmptyCells = current.Histories.slice(0, 12);
     current.emptyCells =  srvEmptyCells.getEmptyCells(current.nonEmptyCells.length);
   }
});

then change your ng-repeats in the html to:

<td ng-repeat="history in data.nonEmptyCells" class="my-table-cell">

and

<td ng-repeat="emptyCell in data.emptyCells" class="empty"></td>

updated plunkr: http://plnkr.co/edit/92S2rNNhBEyXZVsCI2M8?p=preview

Sebastian Piu
  • 7,838
  • 1
  • 32
  • 50
  • Thanks for the input, unfortunately i cant change what data is being returning. The cache solution sounds interesting, any examples? – Oam Psy Oct 28 '14 at 12:04
  • 1
    check now, there might be a way of doing this by calling .memoize function but I have to leave now – Sebastian Piu Oct 28 '14 at 12:10
  • Thanks for the example, can the for loop be placed in my Controller rather than Factory? Memoization looks interesting, but i've never used it and wouldnt know where to start? Would it be simple in this case? – Oam Psy Oct 28 '14 at 12:15
  • Just realised that is in the controller – Oam Psy Oct 28 '14 at 12:28
  • Updated my plunker: http://plnkr.co/edit/2UKDD1fvfYGMjX9oJqVu?p=preview but is not repeating. Can see correct values coming in the console.log. – Oam Psy Oct 28 '14 at 13:22
  • 1
    I fixed the response, it should be `data.emptyCells` in the `ng-repeat` – Sebastian Piu Oct 28 '14 at 13:26
  • are you able to update the Plunker? I cant see where data. is coming from? – Oam Psy Oct 28 '14 at 13:31
  • 1
    here is the plunkr updated: http://plnkr.co/edit/92S2rNNhBEyXZVsCI2M8?p=preview. You had some errors when you copied the answer – Sebastian Piu Oct 28 '14 at 13:47
1

I believe you can reduce the number of ng-repeat's to only 2.

You just need to change a little bit your approach. Instead of doing splice on every ng-repeat, use a list/array of indexes with a predefined length. Then, iterate over that list of indexes and for each index check if there is an item in the same position of the data.Histories array. If there is, then it's a 'my-table-cell' otherwise it's an 'empty' cell (ng-class helps you defining that).

Then use the same logic for the fields, instead of having 1 ng-repeat for each field, try to define a list/array of fields and then use only 1 ng-repeat to iterate over that list.

The final result should be something like this:

<table ng-repeat="data in myData" class="my-table">
  <caption>
    {{ data.Name }}
  </caption>
  <tbody>
    <tr ng-repeat="field in fields">
      <th>{{field}}</th>
      <td ng-repeat="idx in indexes" ng-class="{ 'my-table-cell': data.Histories[idx][field], 'empty': !data.Histories[idx][field]}">
        {{data.Histories[idx][field]}}
      </td>
    </tr>
  </tbody>
</table>

And on the MainCtrl add the following lines:

// the list of indexes could be populated dynamically...
$scope.indexes = [0,1,2,3,4,5,6,7,8,9,10,11];
$scope.fields = ['Rate', 'Effort', 'Advance', 'Previous', 'Current', 'Code'];

Demo

bmleite
  • 26,850
  • 4
  • 71
  • 46
  • @bmliete - Wow! Great option.. What if on the front end i wanted to display 'Previous Amount' instead of 'Previous', but cant change my JSON file. – Oam Psy Oct 28 '14 at 13:51
  • 1
    Yes you can change the order as you want. To display a different label just use Objects instead of Strings and do some little changes on the HTML. Check this [plunkr](http://plnkr.co/edit/s32GHTgQ1Il41dbwMtQ0?p=preview). – bmleite Oct 28 '14 at 13:58
  • Thanks! Any reason why when a cell is null it is not being darkened on the front end? – Oam Psy Oct 28 '14 at 14:46
  • 1
    Sorry, forgot to add the `field` validation in the `ng-class` expression. Check the [plunkr](http://plnkr.co/edit/s32GHTgQ1Il41dbwMtQ0?p=preview) now. – bmleite Oct 28 '14 at 14:55
  • I think your approach is great. When writing Angular apps i use this question to ensure my watches are kept to a minimum: http://stackoverflow.com/questions/18499909/how-to-count-total-number-of-watches-on-a-page After i added a couple of ng-if's because i need to add classes and data-contents for certain labels, the number of watches nearly treble, here's a plunker: http://plnkr.co/edit/n9facqC0OcMS4UpEvMDF?p=preview the page load is naturally very slow as a result. – Oam Psy Oct 29 '14 at 11:35