14

I have a very simple report in AngularJS:

<div class="gridHeader">
   <div>User</div>
   <div>Date</div>
   <div>Count</div>
</div>
<div class="gridBody"
   <div class="gridRow" ng-repeat="row in rps.reports">
      <div>{{row.user}}</div>
      <div>{{row.date}}</div>
      <div>{{row.count}}</div>
   </div>
</div>

The report works but it's difficult to notice when the date changes.

Is there some way that I could assign a class to the grid row so that one date grid row has one class and the next date the grid row has another class. I think this is already available for odd and even rows with Angular but here I need it to work on every date change.

7 Answers7

6

I've done a different solution (PLUNKER) where whole work is inside the controller. Maybe is a little bit more of code, but you will gain a lot of performance if you have thousand records because you will avoid dirty checking of ng-class.
Additionally if your report is static and it won't have any changes, you can disable the two data binding...

CONTROLLER

vm.reports = addReportCssClases();

function addReportCssClases() {
    var reportsData = [...]; 
    var classes = ['dateOdd', 'dateEven'];
    var index = 0; 

    reportsData[0].cssClass = classes[index];

    for (var i = 1; i < reportsData.length; i++) {
      var row = reportsData[i]; 
      index = (row.date !== reportsData[i-1].date) ? (1 - index) : index;
      row.cssClass = classes[index] ;
    }

    return reportsData;
}

HTML

<div ng-repeat="row in vm.reports track by $index" class="gridRow {{::row.cssClass}}">
    <div>{{::row.user}}</div>
    <div>{{::row.date}}</div>
    <div>{{::row.count}}</div>
</div>
The.Bear
  • 5,621
  • 2
  • 27
  • 33
5

You can use ng-class with a function defined in your controller. For example:

var currentColor = "color1";

$scope.getClass = function(index)
{
  if (index !== 0 && $scope.data[index].date !== $scope.data[index - 1].date)
  {
    currentColor = currentColor == "color1" ? "color2" : "color1";
  }

  return currentColor;
}

And in your template:

<div class="gridRow" ng-repeat="(i, d) in data" data-ng-class="getClass(i)">

See the plunker: http://plnkr.co/edit/PPPJRJJ1jHuJOgwf9lNK

ssougnez
  • 5,315
  • 11
  • 46
  • 79
  • 1
    If `rps.reports` is a long list of elements this might impact performance. Otherwise it is a good approach. – lealceldeiro Mar 09 '17 at 16:12
  • @ssougneq - Thank you. I will check this out and also wait for a short while and see if others have a different answer. –  Mar 10 '17 at 06:46
3

This can be done with a one line given all the dates are actually date with the same format and not date time

<div class="gridHeader">
   <div>User</div>
   <div>Date</div>
   <div>Count</div>
</div>
<div class="gridBody">
   <div  ng-repeat="row in rps.reports" class="gridRow"  ng-class="{'backblue':$index>0 && row.date!=rps.reports[$index-1].date}">
      <div>{{row.user}}</div>
      <div>{{row.date}}</div>
      <div>{{row.count}}</div>
   </div>
</div>

your gridRow class will have to contain the background-color

.gridRow{
  //other styles
  background-color:red;
}

and the class backblue will have to have only the background-color

.backblue{
   background-color:blue !important;
}

IMPORTANT This will only work if the date field is only date and does not have time. If in any case it does have time you will have to convert each datetime to date

Raj Nandan Sharma
  • 3,694
  • 3
  • 32
  • 42
1

A modified version of @ssougnez answer by storing the current date also in addition to color:

if(!(currentDate && currentDate === data.date)){
  currentColor = currentColor == "color1" ? "color2" : "color1";
  currentDate = data.date;
}

Plunker: http://plnkr.co/edit/o3YVBB

This might have less impact on performance than his version.

Keerthi Kumar P
  • 1,554
  • 9
  • 14
1

var app = angular.module('sample', []);

app.controller('SampleController', function($scope) 
{
 $scope.data = [
    {
      user: "A",
      date: "3/2/2017"
    },
    {
      user: "B",
      date: "3/4/2017"
      
    },
    {
      user: "C",
      date: "4/3/2017"
    },
      {
      user: "D",
      date: "4/3/2017"
    },
      {
      user: "E",
      date: "4/3/2017"
    },
      {
      user: "F",
      date: "4/2/2017"
    }  
  ];
  
});
.same{
background-color:#ddd;

}

.differ{
background-color:yellow;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<html ng-app="sample">
<head>

</head>
<body>


<div ng-controller="SampleController">
<table id="myTable">
<thead>
<tr>
   <th>User</th>
   <th>Date</th>
 </tr>
</thead>
<tbody>
   <tr ng-repeat="row in data">
      <td>{{row.user}}</td>
      <td class="same" ng-if="data[$index+1].date==row.date || data[$index-1].date==row.date">{{row.date}}</td>
      <td class="differ" ng-if="data[$index+1].date!=row.date && data[$index-1].date!=row.date">{{row.date}}</td>
   </tr>
</tbody>
</table>
</div>
</body>
</html>
Dilip Belgumpi
  • 658
  • 4
  • 13
1

I have created a very simple and working solution for this using angular-filter, only you need to add dynamic class on gridRow.

working jsfiddle

HTML

<div ng-repeat="row in rps.reports" 
    class="gridRow {{row.date | filterDate}}">

Styles

.even {
  background-color: green;
  color: #fff;
}
.odd {
  background-color: red;
  color: #000;
}

Angular-filter

myApp.filter('filterDate', function() {
  var lastDate,
    count = 0,
    calssName = 'even';
  return function(date) {
    var newDate = new Date(date).toDateString();
    !lastDate && (lastDate = newDate);
    if (newDate != lastDate) {
      if (calssName == 'even') {
        calssName = 'odd';
      } else {
        calssName = 'even';
      }
    }
    lastDate = newDate;
    return calssName;
  }
});
Gaurav Kumar Singh
  • 1,550
  • 3
  • 11
  • 31
1

You can add the class with an expression using the ngClass directive in the view:

(function() {

  'use strict';

  angular.module('app', []);

})();

(function() {

  'use strict';

  angular.module('app').controller('MainController', MainController);

  MainController.$inject = ['$scope'];

  function MainController($scope) {

    var date = new Date();

    $scope.rps = {
      reports: [{
          user: 'User A',
          date: date.setDate(date.getDate() + 1),
          count: 5
        },
        {
          user: 'User B',
          date: date.setDate(date.getDate() + 2),
          count: 10
        },
        {
          user: 'User C',
          date: date.setDate(date.getDate()),
          count: 8
        },
        {
          user: 'User D',
          date: date.setDate(date.getDate() + 2),
          count: 6
        },
        {
          user: 'User E',
          date: date.setDate(date.getDate()),
          count: 20
        },
        {
          user: 'User F',
          date: date.setDate(date.getDate() + 3),
          count: 6
        }
      ]
    };

  }

})();
.gridHeader,
.gridRow {
  display: table-row;
}

.gridHeader>div,
.gridRow>div {
  display: table-cell;
  padding: 5px 10px;
}

.className {
  background: #ff0000;
  color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>

<div ng-app="app" ng-controller="MainController as MainCtrl">

  <div class="gridHeader">
    <div>User</div>
    <div>Date</div>
    <div>Count</div>
  </div>
  <div class="gridBody">
    <div class="gridRow" ng-repeat="row in ::rps.reports track by $index" ng-class="::{'className': $index === 0 || $index > 0 && rps.reports[$index - 1].date !== row.date}">
      <div>{{::row.user}}</div>
      <div>{{::row.date | date: 'dd-MM-yyyy'}}</div>
      <div>{{::row.count}}</div>
    </div>
  </div>

</div>

Or add a boolean in the controller that you can use to trigger the className using the ngClass directive:

(function() {

  'use strict';

  angular.module('app', []);

})();

(function() {

  'use strict';

  angular.module('app').controller('MainController', MainController);

  MainController.$inject = ['$scope'];

  function MainController($scope) {

    var date = new Date();

    $scope.rps = {
      reports: [{
          user: 'User A',
          date: date.setDate(date.getDate() + 1),
          count: 5
        },
        {
          user: 'User B',
          date: date.setDate(date.getDate() + 2),
          count: 10
        },
        {
          user: 'User C',
          date: date.setDate(date.getDate()),
          count: 8
        },
        {
          user: 'User D',
          date: date.setDate(date.getDate() + 2),
          count: 6
        },
        {
          user: 'User E',
          date: date.setDate(date.getDate()),
          count: 20
        },
        {
          user: 'User F',
          date: date.setDate(date.getDate() + 3),
          count: 6
        }
      ]
    };

    // add the classes to the reports
    addClasses($scope.rps.reports);

  /*
   * @name addClasses
   * @type function
   * 
   * @description
   * Adds a class to a report if the date is different to the previous
   *
   * @param {array} reports The reports to add classes to
   * @return nothing.
   */
    function addClasses(reports) {

      // loop through the reports to check the dates
      for (var i = 0, len = reports.length; i < len; i++) {

        // if the previous report a different date then the current report will have a class
        reports[i].hasClass = (i === 0 || reports[i - 1].date !== reports[i].date);

      }

    }

  }

})();
.gridHeader,
.gridRow {
  display: table-row;
}

.gridHeader>div,
.gridRow>div {
  display: table-cell;
  padding: 5px 10px;
}

.className {
  background: #ff0000;
  color: #fff;
}
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.3/angular.min.js"></script>

<div ng-app="app" ng-controller="MainController as MainCtrl">

  <div class="gridHeader">
    <div>User</div>
    <div>Date</div>
    <div>Count</div>
  </div>
  <div class="gridBody">
    <div class="gridRow" ng-repeat="row in ::rps.reports track by $index" ng-class="::{'className': row.hasClass}">
      <div>{{::row.user}}</div>
      <div>{{::row.date | date: 'dd-MM-yyyy'}}</div>
      <div>{{::row.count}}</div>
    </div>
  </div>

</div>
cnorthfield
  • 3,384
  • 15
  • 22