Angular2 table rows as component

Angular

Angular Problem Overview


I am experimenting with angular2 2.0.0-beta.0

I have a table and the line content is generated by angular2 this way:

    <table>
        <tr *ngFor="#line of data">
            .... content ....
        </tr>
    </table>

Now this works and I want to encapsulate the content into a component "table-line".

    <table>
        <table-line *ngFor="#line of data" [data]="line">
        </table-line>
    </table>

And in the component, the template has the <tr><td> content.

But now the table does no more work. Which means, the content is no longer shown in columns. In the browser, the inspector shows me that the DOM elements look like this:

    <table>
        <table-line ...>
            <tbody>
                <tr> ....

How can I make this work?

Angular Solutions


Solution 1 - Angular

use existing table elements as selector

The table element doesn't allow <table-line> elements as children and the browser just removes them when it finds them. You can wrap it in a component and still use the allowed <tr> tag. Just use "tr" as selector.

using <template>

<template> should be allowed as well but doesn't yet work in all browsers. Angular2 actually never adds a <template> element to the DOM, but only processes them internally, therefore this can be used in all browsers with Angular2 as well.

Attribute selectors

Another way is to use attribute selectors

@Component({
  selector: '[my-tr]',
  ...
})

to be used like

<tr my-tr>

Solution 2 - Angular

I found the example very usefull but it didn't work in the 2,2.3 build, so after much head scratching made it work again with a few small changes.

import {Component, Input} from '@angular/core'

@Component({
  selector: "[my-tr]",
  template: `<td *ngFor='let item of row'>{{item}}</td>`    
})
export class MyTrComponent {
  @Input("line") row:any;
}

@Component({
  selector: "my-app",
  template: `<h1>{{title}}</h1>
  <table>
    <tr  *ngFor="let line of data" my-tr [line]="line"></tr>
  </table>`

})
export class AppComponent {

  title = "Angular 2 - tr attribute selector!";
  data = [ [1,2,3], [11, 12, 13] ];
  constructor() { console.clear(); }
}

Solution 3 - Angular

Here's an example using a component with an attribute selector:

import {Component, Input} from '@angular/core';
@Component({
  selector: '[myTr]',
  template: `<td *ngFor="let item of row">{{item}}</td>`
})
export class MyTrComponent {
  @Input('myTr') row;
}
@Component({
  selector: 'my-app',
  template: `{{title}}
  <table>
    <tr *ngFor="let line of data" [myTr]="line"></tr>
  </table>
  `
})
export class AppComponent {
  title = "Angular 2 - tr attribute selector";
  data = [ [1,2,3], [11, 12, 13] ];
}

Output:

1	2	3
11	12	13

Of course the template in the MyTrComponent would be more involved, but you get the idea.

Old (beta.0) plunker.

Solution 4 - Angular

Adding 'display: contents' to the component style worked out for me.

CSS:

.table-line {
    display: contents;
}

HTML:

<table>
    <table-line class="table-line" [data]="line">
    </table-line>
</table>

Why this works?

When instancing a component, angular (after compiling) wraps the content of the component in the DOM as follows:

<table>
    <table-line>
        <tr></tr>
    </table-line>
</table>

But in order for the table to display properly, the tr tags can't be wrapped by anything.

So, we add display: contents, to this new element. As I understand, what this does is to tell the explorer that this tag should not be rendered, and display the inner content as if there was no wrapping around. So, while the tag still exists, it doesn't affect visually to the table, and the tr tags are treated as if they were direct children of the table tag.

If you'd like to investigate further on how contents works: https://bitsofco.de/how-display-contents-works/

Solution 5 - Angular

try this

@Component({
    selecctor: 'parent-selector',
    template: '<table><body><tra></tra></body></table>'
    styles: 'tra{ display:table-row; box-sizing:inherit; }'
})
export class ParentComponent{
}

@Component({
    selecctor: 'parent-selector',
    template: '<td>Name</td>Date<td></td><td>Stackoverflow</td>'
})
export class ChildComponent{}

Attributions

All content for this solution is sourced from the original question on Stackoverflow.

The content on this page is licensed under the Attribution-ShareAlike 4.0 International (CC BY-SA 4.0) license.

Content TypeOriginal AuthorOriginal Content on Stackoverflow
QuestionfbenoitView Question on Stackoverflow
Solution 1 - AngularGünter ZöchbauerView Answer on Stackoverflow
Solution 2 - AngularDudeOfDoomView Answer on Stackoverflow
Solution 3 - AngularMark RajcokView Answer on Stackoverflow
Solution 4 - AngularRuben NegredoView Answer on Stackoverflow
Solution 5 - AngularMatsteelView Answer on Stackoverflow