1

I really don't know how to succinctly describe what I'm trying to do so sorry for the bad title. But I have a table where I want to interlace different types of rows. I have one row that mirrors the columns across the top, and I have another row that spans all columns to show the year.

For example:

<table>
  <thead>...</thead>
  <tbody>
     <tr>
         <td>11/30/2013</td>
         <td>Some Description</td>
         <td>$1450.00</td>
         ...
     </tr>
     <tr>
         <td>12/31/2013</td>
         <td>Some Description</td>
         <td>$1450.00</td>
         ...
     </tr>
     <tr colspan="9">2014</tr>
     <tr>
         <td>1/31/2014</td>
         <td>Some Description</td>
         <td>$1450.00</td>
         ...
     </tr>
   </tbody>
</table>

Here is the straightforward implementation to put all of the data in the table:

<table>
  <thead>...</thead>
  <tbody>
      <tr ng-repeat="paycheck in paychecks">
         <td>{{paycheck.payDate}}</td>
         <td>{{paycheck.description}}</td>
         <td>{{paycheck.amount}}</td>
         ...
      </tr>
  </tbody>
</table>

However, I'd like to put this row between those rows when I traverse over a yearly boundary:

<tr>
   <td colspan="9" ng-if="paycheck.payDate.year > previousPaycheck.payDate.year">{{paycheck.payDate.year}}</td>
</tr>

This means I'd have to put two rows in the table when the above condition is true, but with ng-repeat I'm only adding one. I suspect I'll have to rebuild my data structure such that I insert the year boundaries in it so ng-repeat will be a 1:1 with rows in the table and elements in my array. Or maybe some special directive magic that could make this easier.

Is there an easy way to do this that I don't see?

chubbsondubs
  • 37,646
  • 24
  • 106
  • 138
  • can you post some sample data for paychecks object please – guru Jun 06 '14 at 14:14
  • The data in paychecks isn't material to the answer because it's a bunch of properties (one of which is the date as illustrated above), and those properties are put in a series of columns for each row. I've included enough of the properties in the example for you to guess what the data looks like. – chubbsondubs Jun 06 '14 at 14:38

3 Answers3

1

You can use ng-repeat-start and ng-repeat-end.

<tbody>
      <tr ng-repeat-start="paycheck in paychecks">
        <td colspan="9" ng-if="paycheck.payDate.year > paychecks[$index-1].payDate.year">  {{paycheck.payDate.year}}</td>
      </tr>
      <tr ng-repeat-end>
         <td>{{paycheck.payDate}}</td>
         <td>{{paycheck.description}}</td>
         <td>{{paycheck.amount}}</td>
      </tr>          
</tbody>

Plunker

Jerrad
  • 5,240
  • 1
  • 18
  • 23
0

ng-repeat has a few variables it attaches to the scope on each iteration, one being $index.

Leveraging this, you could do something like:

<table>
    <tbody ng-repeat="date in data">
      <tr ng-show='$index == 0 || data[$index].year != data[$index-1].year'>
        <td colspan="3">{{date.year}}</td>
      </tr>
      <tr>
        <td>{{date.year}}</td>
        <td>{{date.month}}</td>
        <td>{{date.day}}</td>
      </tr>
    </tbody>
</table>

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

Note that this requires your data to be sorted.

Manny D
  • 20,310
  • 2
  • 29
  • 31
  • This will give you a separate `tbody` for each paycheck, which may not be what you want. – Jerrad Jun 06 '14 at 14:54
  • A minor detail that's irrelevant to the solution. – Manny D Jun 06 '14 at 14:58
  • Not irrelevant if you have css styling the table that isn't expecting multiple tbody tags. – Jerrad Jun 06 '14 at 14:59
  • I'm merely suggesting the asker to look at the logic, not to copy/paste the solution into his own work. – Manny D Jun 06 '14 at 15:01
  • Maybe the asker actually _wants_ multiple groups in the table, in which case this method would be preferable. But his desired output only indicates a single `tbody`. – Jerrad Jun 06 '14 at 15:04
  • No Jerrad is correct. Not having extra nodes is important to the solution so not having multiple tbodies or multiple tr tags that are just hidden is a no go. I could figure that those solutions on my own. I knew how to do it inefficiently. – chubbsondubs Jun 06 '14 at 15:48
0

Interesting question.

Here is a working solution in plunker: http://plnkr.co/edit/a55krX8x9G1e5MGUIz0A?p=preview

Logic: Firstly the data has to be in the ordered by the payDate for this to work

   <tbody ng-repeat="paycheck in paychecksOrdered">
      <tr ng-show="header(paycheck, $index)">
        <td colspan="9">{{paycheck.payDate | date:'yyyy'}}</td>
      </tr>
      <tr>
        <td>{{paycheck.payDate}}</td>
        <td>{{paycheck.description}}</td>
        <td>{{paycheck.amount}}</td>
      </tr>
    </tbody>



  $scope.paychecksOrdered = $filter('date')(paychecks, 'payDate');
  $scope.header = function(paycheck, i) {
    if (i === 0) {
      return true;
    }
    console.log(i);
    var past = $scope.paychecksOrdered[i - 1].payDate.getFullYear(),
      cur = paycheck.payDate.getFullYear();
    console.log(cur, past);
    return past != cur;
  };
guru
  • 4,002
  • 1
  • 28
  • 33
  • Like the other answer, this will give you a separate `tbody` for each paycheck, which may not be what you want. – Jerrad Jun 06 '14 at 14:57
  • Using ng-repeat on tbody appears to be valid see this post -http://stackoverflow.com/questions/3076708/can-we-have-multiple-tbody-in-same-table – guru Jun 06 '14 at 15:00
  • Not saying it isn't valid, just saying you might end up with a table that you aren't expecting. – Jerrad Jun 06 '14 at 15:01
  • 1
    I'm ok with either wise to use ng-repeat or using start/end tags. And this is just a solution to the question. And even html validators permit multiple tbody elements inside a table. Though I'm not sure how this will result in an unexpected table. Applying CSS will be trivial here I guess. – guru Jun 06 '14 at 15:05
  • Why fill the DOM with unnecessary tags? – Jerrad Jun 06 '14 at 15:12
  • I'm looking for something that doesn't repeat the tbody element and inserts the minimal amount of nodes. Using ng-show/hide will double the count of nodes in the DOM for the size of paychecks. I want something more efficient which Jerrad answered. – chubbsondubs Jun 06 '14 at 15:54