1

I need to create a table from some data with multiple levels of arrays and sub-arrays. I have found some solutions to do this as long as I only have two levels of arrays, but none that would work with anything more than that.

For example, take the sample data below --

$scope.professors = [{
    'name': 'Albert Einstein',
    'classes': [{
        'name': 'Physics 101',
        'students': [{
            'name': 'Joe',
            'grade': 'B'
        }, {
            'name': 'Mary',
            'grade': 'A'
        }]
    }, {
        'name': 'Physics 201',
        'students': [{
            'name': 'Gunther',
            'grade': 'C'
        }, {
            'name': 'Hans',
            'grade': 'C'
        }]
    }]
}, {
    'name': 'Charles Darwin',
    'classes': [{
        'name': 'Biololgy 101',
        'students': [{
            'name': 'Danielle',
            'grade': 'A'
        }, {
            'name': 'Anne',
            'grade': 'A'
        }]
    }, {
        'name': 'Biology 201',
        'students': [{
            'name': 'Frida',
            'grade': 'A'
        }, {
            'name': 'Fritz',
            'grade': 'F'
        }]
    }]
}];

You have some professors each with some disciplines each in turn with some students enrolled. I want to create a table-report that show all professors along with all their disciplines and students and grades. In order to do this, I would need a table such as the one below --

<table>
    <tbody>
        <tr>
            <th rowspan="4">Albert Einstein</th>
            <th rowspan="2">Physics 101</th>
            <td>Joe</td>
            <td>B</td>
        </tr>
        <tr>
            <td>Mary</td>
            <td>A</td>
        </tr>
        <tr>
            <th rowspan="2">Physics 201</th>
            <td>Gunther</td>
            <td>C</td>
        </tr>
        <tr>
            <td>Hans</td>
            <td>C</td>
        </tr>
        <tr>
            <th rowspan="4">Charles Darwin</th>
            <th rowspan="2">Biology 101</th>
            <td>Danielle</td>
            <td>A</td>
        </tr>
        <tr>
            <td>Anne</td>
            <td>A</td>
        </tr>
        <tr>
            <th rowspan="2">Biology 201</th>
            <td>Frida</td>
            <td>A</td>
        </tr>
        <tr>
            <td>Fritz</td>
            <td>F</td>
        </tr>
    </tbody>
</table>

i.e.

|------------------|-------------|----------|---|
| Albert Einstein  | Physics 101 | Joe      | B |
|                  |             | Mary     | A |
|                  | ------------|----------|---|
|                  | Physics 201 | Gunther  | C |
|                  |             | Hans     | C |
|------------------|-------------|----------|---|
| Charles Darwin   | Biology 101 | Danielle | A |
|                  |             | Anne     | A |
|                  |-------------|----------|---|
|                  | Biology 201 | Frida    | A |
|                  |             | Fritz    | F |
|------------------|-------------|----------|---|

The solutions I've found generate multiple tbody elements with ng-repeat for each professor (in this case) and then another ng-repeat on a tr for each "class" for that professor. Like --

<table>
    <tbody ng-repeat="prof in professors">
        <tr ng-repeat="c in prof.classes">
            <th ng-if="$first" rowspan="{{prof.classes.length}}">{{prof.name}}</th>
            <td>{{c.name}}</td>
        </tr>
    </tbody>
</table>

Others use ng-repeat-start and ng-repeat-end but none more than two levels deep. Is there a way to add one more level—in this case, the students—to this?

6
  • why can't you just do s in prof.classes.students? Commented Nov 21, 2014 at 23:00
  • Not sure I understand. In what element would I use that? Also, prof.classes is an array, it doesn't have a students property. Or am I missing something? Commented Nov 21, 2014 at 23:02
  • 1
    In $scope.professors you have a students array in the classes array. the ng-repeat attributes are just looping through that, so in your td with c.name make a new table with ng-repeat="s in prof.classes.students" Commented Nov 21, 2014 at 23:05
  • I updated to make it a better example. Anyway, I see your point; I can definitely simply use a nested table with s in c.students but that's not really what I'm looking for. I really would like to create a table like the one I posted as an example. Maybe it isn't possible with ng-repeat? I think I would need some way to use ng-repeat without creating child elements so that I would only create the tr elements inside the innermost ng-repeat (in my case, students). Thanks though! Commented Nov 21, 2014 at 23:15
  • Are you generating some report that will be printed? If not this layout feels like it could become too busy. Have you cosidered using an expandable tree to display the next level of data as needed? Commented Nov 22, 2014 at 8:07

1 Answer 1

3

After much frustration and head scratching, I found a way to do it using ng-repeat-start

<table>
    <tbody>
        <tr ng-repeat-start="p in professors" ng-if="false"></tr>
        <tr ng-repeat-start="c in p.classes" ng-if="false"></tr>
        <tr ng-repeat="s in c.students">
            <th ng-if="$parent.$first && $first" rowspan="{{p.count}}">
                {{p.name}}
            </th>
            <th ng-if="$first" rowspan="{{c.students.length}}">{{c.name}}</th>
            <td>{{s.name}}</td>
            <td>{{s.grade}}</td>
        </tr>
        <tr ng-repeat-end ng-if="false"></tr> <!-- classes -->
        <tr ng-repeat-end ng-if="false"></tr> <!-- professors -->
    </tbody>
</table>

This solution generates valid HTML table markup. By using ng-if="false" on the ng-repeat-* elements, they iterate through the arrays but generate no actual element on the page. The only elements generated are the tr rows in s in c.students.

The other thing I needed to do is create a new property inside the professor objects named count that holds the total number of students enrolled in all the classes. I needed that for the rowspan and it's of course trivial to generate on the fly when I get data from the server.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.