Dynamically adding rows using components into HTML table with Angular - html

I'm encountering a strange problem and I cannot understand why it's happening.
I want to render an HTML table using Angular 4.
If I output the table using the following syntax everything works fine
<tbody>
<tr *ngFor="let athlete of athletes">
<td></td>
<td>{{athlete.name}}</td>
<td>{{athlete.country}}</td>
<td>{{athlete.time}}</td>
</tr>
</tbody>
And the table is rendered correctly:
But if do the same thing delegating the rending of the row to a component the table is not rendered correctly in Chrome (but is correct in Edge).
<tbody>
<app-athlete [athlete]="athlete" *ngFor="let athlete of athletes">
</app-athlete>
</tbody>
athlete.component.html
<tr>
<td></td>
<td>{{athlete.name}}</td>
<td>{{athlete.country}}</td>
<td>{{athlete.time}}</td>
</tr>
Could it be due to the fact that the "rendered" DOM is something like this when looking at Google Dev tools?
I also tried putting the <tr> with the *ngFor outside of the component but the problem is always the same.
I've reproduced the problem on Plunkr: https://plnkr.co/FZAFEP5S8KhzvdKwBtbH

Use attribute selector for your component
athlete.component.ts
#Component({
selector: 'tr[app-athlete]',
template: `
<td></td>
<td>{{athlete.name}}</td>
<td>{{athlete.country}}</td>
<td>{{athlete.time}}</td>
`,
})
export class AthleteComponent {
#Input() athlete: Athlete;
}
Now template of parent component should look like:
<tr app-athlete [athlete]="athlete" *ngFor="let athlete of athletes">
</tr>
Plunker Example

Related

Access iterable in sibling tag

As stated i want to access an iterable from a sibling tag. Unfortunatly its in a table so i cant just do it by wrapping the stuff with a diff and push the iteration one lvl outside. Also the items are tablerows so i cant make the sibling to a child. I guess this is easier with an example(thats what i want to do):
<table>
<tr v-for="element in elements">
...
</tr>
<tr v-for="hit in element.hits">
...
</tr>
</table>
Obviously this doesnt work since once i close the first tr i drop out of scope. Is there any work around that?
Regardiest regards,
Sean
v-for can be used on <template> that wraps <tr>, in this case it isn't rendered itself:
<template v-for="element in elements">
<tr>
...
</tr>
<tr v-for="hit in element.hits">
...
</tr>
</template>

Custom Component injected into table row

I have a table that is created in one componentA that has several table-row elements in the table. I then have another componentB that just has several table-row elements that will be reused throughout the app in the template code. The issue is that when using componentB inside of the table of componentA the table-row elements are not spanning the entire width of the table and seem to be broken outside of the table.
I have found references to several other similar issues, but they are using dynamic rows based on data that is passed in for each row. My issue is that I have a fixed set of rows that will be used throughout the app. I tried using the following examples:
--- componentA
<table>
<tr><td>Name</td></tr>
<tr componentB></tr> --- does not work
</table>
--- componentB --- selector: '[componentB]'
<tr><td>Phone Number</td></tr>
<tr><td>Email</td></tr>
--- componentA
<table>
<tr><td>Name</td></tr>
<componentB></componentB> --- does not work
</table>
--- componentB --- selector: 'componentB'
<tr><td>Phone Number</td></tr>
<tr><td>Email</td></tr>
Here is an example stackblitz of exactly the problem: https://stackblitz.com/edit/angular-ydsgaz
The elements in componentB should span the width of the table just like a normal element used in componentA, but they are not. Any help would be great!
ANSWER: either one of the following examples will work for this scenario
content-projection: https://stackblitz.com/edit/angular-dr6fqs
template reference (my preferred way): https://stackblitz.com/edit/angular-qgxzhv
Use angular content projection.
You just have to add a directive selector to table body.
app.component.html
<table>
<tbody app-table-rows>
<tr>
<td>name</td>
<td>bob</td>
</tr>
<tbody>
</table>
and then in table-rows.component.html add ng-content element. Angular will replace this ng-content element with content inside directive host element.
<ng-content></ng-content>
<tr>
<td>email</td>
<td>bob#gmail.com</td>
</tr>
...
Working example https://stackblitz.com/edit/angular-dr6fqs

Checkboxes disappear when materializecss is applied (using django)

The code with the css makes the checkboxes disappear for cash.
They have been initialised as boolean variables in the model(using django).
<table>
<thead>
<tr>
<th>Payment Method:</th>
</tr>
</thead>
<tbody>
<tr>
<td>{{ seller_event_form.cash_payment }}</td>
</tr>
</tbody>
</table>
Without the css, the checkboxes appear. Because of this, none of the data inputted gets stored in the database.
edit with more info
I'm trying to unpack a model form here. When I unpack without adding any css, the boolean value in the model gets converted to a checkbox in the html. However, when I add css, the checkbox disappears. What am I doing wrong?

Using ngIf without an extra element in Angular 2

Can I use ngIf without an extra container element?
<tr *ngFor="...">
<div *ngIf="...">
...
</div>
<div *ngIf="!...">
...
</div>
</tr>
It doesn't work in a table because that would make invalid HTML.
ng-container is preferred over template:
<ng-container *ngIf="expression">
See:
Angular 2 ng-container
https://github.com/angular/angular.io/issues/2303
I found a method for that on: https://angular.io/docs/ts/latest/guide/template-syntax.html#!#star-template.
You can simply use the <template> tag and replace *ngIf with [ngIf] like this.
<template [ngIf]="...">
...
</template>
You can't put div directly inside tr, that would make invalid HTML. tr can only have td/th/table element in it & inside them you could have other HTML elements.
You could slightly change your HTML to have *ngFor over tbody & have ngIf over tr itself like below.
<tbody *ngFor="...">
<tr *ngIf="...">
...
</tr>
<tr *ngIf="!...">
...
</tr>
..
</tbody>
adding brackets resolves this issue
<ng-container *ngIf="(!variable| async)"></ng-container>
You can try this:
<ng-container *ngFor="let item of items;">
<tr *ngIf="item.active">
<td>{{item.name}}</td>
</tr>
</ng-container>
Here, I have iterate loop in ng container so it will not create extra dom and later in tr tag check condition if I want to render or not.

*ngFor is not working with dynamic values

Working on some array projects but Stuck on point where *ngFor did't accept dynamic value provided by another *ngFor.
<table border="2">
<tr>
<th rowspan="2">Subject</th>
<th colspan="4" *ngFor='#category of json.category_name'>{{category}}</th>
<th colspan="4">Overall</th>
</tr>
<tr>
<div *ngFor="#category of json.category_name">
<th *ngFor="#fa of json.total_fa.category">{{fa}}</th> //Not Working
</div>
</tr>
</table>
here in the commented line i want to provide json.total_fa.category where this category is coming from another *ngFor which is declared just above. but getting no result it seems angular try to find out json.total_fa.category itself which is not present.
but i want to load json.total_fa_term1 (which is first index from the category). how can i achive this.
Can i use more than one *ngFor on same HTML component ?
sometimes *ngFor and *ngIf is not working properly while embed on the same element why ?
here is my workground demo
You need to use .total_fa[category] instead of .total_fa.category otherwise you will try to access a property with name category:
<div *ngFor="#category of json.category_name">
<th *ngFor="#fa of json.total_fa[category]">{{fa}}</th>
</div>
See the updated plunkr: http://plnkr.co/edit/v08AdzNsR4GHMlZWQNsR?p=preview.