How to use Angular2 templates with *ngFor to create a table out of nested arrays?
Asked Answered
B

8

40

Given the following array in component property groups:

[
   {
     "name": "pencils",
     "items": ["red pencil","blue pencil","yellow pencil"]
   },
   {
     "name": "rubbers",
     "items": ["big rubber","small rubber"]
   },
]

How to create a html table with all items, each in one row? The expected HTML result:

<table>
    <tr><td><h1>pencils</h1></td></tr>
    <tr><td>red pencil</td></tr>
    <tr><td>blue pencil</td></tr>
    <tr><td>yellow pencil</td></tr>
    <tr><td><h1>rubbers</h1></td></tr>
    <tr><td>big rubber</td></tr>
    <tr><td>small rubber</td></tr>
</table>

The first level is easy:

<table>
    <tr *ngFor="#group of groups">
        <td><h1>{{group.name}}</h1></td>
    </tr>
</table>

But now I have to iterate #item of group. The problem is that I need the new <tr> elements after the </tr> element which defines group, not inside.

Is there any solution for this kind of problems in Angular2 templating? I would expect some special tag which I could use instead of <tr> which is not written into the dom. Something similar to facets and fragments in JSF.

Blameless answered 29/2, 2016 at 12:46 Comment(5)
Can you add the HTML output that you expect as result. I don't get it what you mean by "after the </tr>.Jurywoman
I guess you need flatten the nested JSON structure in code first.Jurywoman
That would be verry sad. I would have to create redundant data and keep it in sync :-(Blameless
Angular allows to build custom structural directives like ngIf, ngFor. You could build an ngFor variant that does that ;-)Jurywoman
There is an tbody element too, perfectly suitable for this case.Haze
S
59
<table>
     <ng-container *ngFor="let group of groups">
         <tr><td><h2>{{group.name}}</h2></td></tr>
         <tr *ngFor="let item of group.items"><td>{{item}}</td></tr>
     </ng-container>
</table>
Straight answered 20/5, 2017 at 14:9 Comment(0)
B
27

You can use the template syntax of ngFor on groups and the usual syntax inside it for the actual rows like:

<table>
  <template let-group ngFor [ngForOf]="groups">
    <tr *ngFor="let row of group.items">{{row}}</tr>
  </template>
</table>

Check this plunk

Ballast answered 11/5, 2016 at 11:58 Comment(0)
A
11

Here is a basic approach - it sure can be improved - of what I understood to be your requirement.

This will display 2 columns, one with the groups name, and one with the list of items associated to the group.

The trick is simply to include a list within the items cell.

<table>
  <thead>
    <th>Groups Name</th>
    <th>Groups Items</th>
  </thead>
  <tbody>
    <tr *ngFor="let group of groups">
      <td>{{group.name}}</td>
      <td>
        <ul>
          <li *ngFor="let item of group.items">{{item}}</li>
        </ul>
      </td>
    </tr>
  </tbody>
</table>
Animalist answered 19/12, 2017 at 16:27 Comment(2)
Thx for the missing quote at the end of groups :)Animalist
Just what I needed. Simple and straight-forwardGies
B
2

it's not exact output that you wanted but maybe something like this will do. Parent cmp:

<table>
  <item *ngFor="#i of items" [data]="i"></item>
</table>

Child cmp

import {Component} from 'angular2/core';

@Component({
  selector: `item`,
  inputs: ['data'],
  template: `
    <tr><td>{{data.name}}</td></tr>
    <tr *ngFor="#i of data.items">
      <td><h1>{{i}}</h1></td>
    </tr>
  `
})
export default class Item {


}
Berretta answered 29/2, 2016 at 13:11 Comment(0)
R
2
   <tbody  *ngFor="let defect of items">            
          <tr>
            <td>{{defect.param1}}</td>
            <td>{{defect.param2}}</td>
            <td>{{defect.param3}}</td>                
            <td>{{defect.param4}}</td>
            <td>{{defect.param5}} </td>
            <td>{{defect.param6}}</td>
            <td>{{defect.param7}}</td>           
          </tr>
          <tr>
            <td> <strong> Notes:</strong></td>
            <td colspan="6"> {{defect.param8}}
            </td>`enter code here`
          </tr>          
        </tbody>
Retroaction answered 13/8, 2020 at 12:49 Comment(1)
Please add some explanation to your answer, such as how it works and solves the problem.Extremist
M
1

Try this. The scope of local variables defined by "template" directive.

<table>
  <template ngFor let-group="$implicit" [ngForOf]="groups">
    <tr>
      <td>
        <h2>{{group.name}}</h2>
      </td>
    </tr>
    <tr *ngFor="let item of group.items">
                <td>{{item}}</td>
            </tr>
  </template>
</table>
Marney answered 11/5, 2016 at 13:35 Comment(0)
S
0

I am a fan of keeping logic out of the template as much as possible. I would suggest creating a helper function that returns the data that you care about to the template. For instance:

getItemsForDisplay():String[] {
 return [].concat.apply([],this.groups.map(group => group.items));
};

<tr *ngFor="let item of getItemsForDisplay()"><td>{{item}}</td></tr>

This will let you keep your presentation free of special logic. This also lets you use your datasource "directly".

Saratov answered 30/9, 2016 at 15:34 Comment(0)
B
0

This worked for me.

<table>
  <tr>
    <td *ngFor="#group of groups">
       <h1>{{group.name}}</h1>
   </td>
  </tr>
</table>
Bedesman answered 8/2, 2017 at 22:52 Comment(1)
This worked for me <td *ngFor="let header of blueHeaders" style="width:100px; text-align:center;background-color: #366092"></td>Pesky

© 2022 - 2024 — McMap. All rights reserved.