Angular2 table rows as component
Asked Answered
T

5

121

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?

Thirteenth answered 1/1, 2016 at 12:51 Comment(0)
P
140

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>
Pellikka answered 1/1, 2016 at 13:21 Comment(10)
So I would create my TableItemComponent with a selector = 'tr'. But how can use 'tr' in other places for different issues?Thirteenth
If the parent component includes your custom tr tag it will be used, otherwise it's default browser behavior takes place. You can also add attributes or classes to your component selector like <tr line-item> with a component selector "tr[line-item] " or <tr class="line-item"> with component selector tr.line-item. This way you have full control. I haven't tried any of this myself yet in Angular2, but I'm pretty sure this works.Poona
Anyone got this working ? I'm trying but without any success with angular 2.3. If any of you can point to working plunker it will be priceless.Zenda
That's pretty basic and should just work. Why don't you provide a Plunker that demonstrates your problem?Poona
Is there any real drawback to this? It works fine, however the default settings for tslint direct me to the style guide recommending against this. I dislike turning off recommended linting rules, but as far as I can tell, it seems a fairly arbitrarily style rule and unavoidable in some situations (such as OP's problem)Twelfth
I'm pretty sure there is no drawback, except that element selectors are way more common and therefore should be the default. If there is a valid case like table elements or <li> or similar, I don't see any reason why not to use it.Poona
Making my component selector use tr[line-item] worked and conforms to Angular Style guide rule STYLE 05-03 that tslint complains against.Newmarket
It's just a style rule. If using that were a mistake, they would remove support for it entirely instead of creating a style rule. Your use case is exactly why it's actually supported. Just disable the linter rule.Poona
It may be obvious to many (even to me now), but I tripped over not closing the element with the custom attribute. To be precise: Use <tr my-tr></tr> instead of <tr my-tr></tr my-tr>Obsessive
The my-tr in the closing part of the element is not necessary. The root cause has to be somewhere else.Poona
I
34

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(); }
}
Inflorescence answered 22/12, 2016 at 16:4 Comment(1)
Great answer. I just had to add the MyTrComponent to the app.module.ts and works fine.Undecided
H
28

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.

Houser answered 1/1, 2016 at 17:14 Comment(4)
Is it possible to render ng component without root tag? Something like <!-- /ko --> in Knockout.js.Inkhorn
@PashaPasha, see #34280975Houser
This doesn't work at all(tried with angular 2.3). Error thrown: Error: Uncaught (in promise): Error: Template parse errors: Can't bind to 'myTr' since it isn't a known property of 'tr'.Zenda
To make it work you need to add [ ] on Selector. Don't forget that.Wore
L
27

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/

Lamothe answered 14/5, 2020 at 6:35 Comment(2)
When possible, please make an effort to provide additional explanation instead of just code. Such answers tend to be more useful as they help members of the community and especially new developers better understand the reasoning of the solution, and can help prevent the need to address follow-up questions.Venosity
Hi @Venosity , thanks for the tip. I just discovered how contents works, so I didn't want to put there information based on false assumptions... I added the explanation as I understand it, but if someone notice that there's an error on it please feel free to point it out ^^. I also added a link so people can investigate further... One thing I noticed, while it says that this doesn't work on IE11, I tried it out on IE 11.657.18362.0 and it worked fine (but, since I'm using angular, there might be a polyfill somewhere, so I wouldn't guarantee it 100% on other environments)Lamothe
E
-3

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{}
Emir answered 15/3, 2018 at 17:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.