chubbsondubs
chubbsondubs

Reputation: 38676

How to build a table with ng-repeat with mixed number of columns for row?

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?

Upvotes: 1

Views: 2637

Answers (3)

guru
guru

Reputation: 4022

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;
  };

Upvotes: 0

Jerrad
Jerrad

Reputation: 5290

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

Upvotes: 1

Manny D
Manny D

Reputation: 20714

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.

Upvotes: 0

Related Questions