Reputation: 709
I am working on an application to display the results of testing a website with Java's TestNG. I know it's a bit redundant since TestNG already generates its own site, but it's part of a training exercise for a bootcamp.
I've come upon a peculiar issue while generating a table with *ngFor
. The issue is that if a test comes back failed, it comes back with a stack trace. Because Java stack traces are so verbose, that stack trace is placed on a new row, therefore I cannot put the *ngFor
on the <tr>
tag as one would normally do. I was hesitant to put it on the <tbody>
tag because I feared that that would cause layout issues (and it does) so I tried wrapping the two <tr>
tags in a <div>
and putting the *ngFor
on that. That did not work, because now my table headers weren't aligned with the table's content. I ended up putting the *ngFor
directive on the <tbody>
as that seemed mostly all right, even though it produced some artifacts between my rows as if they have borders.
However I still don't like that approach. Not only is it hacky as it is, it creates some bugs on very large layouts.
<table class="table table-striped table-dark table-hover table-sm table-responsive" *ngIf="loaded">
<thead>
<tr>
<th>Name</th>
<th>Signature</th>
<th>Duration</th>
<th>Started At</th>
<th>Finished At</th>
<th>Status</th>
</tr>
</thead>
<tbody *ngFor="let method of testResults.tests; let index = index">
<tr>
<td>{{ method.name | camelToTitle }}</td>
<td>{{ method.signature }}</td>
<td>{{ method.duration_ms }} ms</td>
<td>{{ method.startTime | dates }}</td>
<td>{{ method.finishTime | dates }}</td>
<td
[ngStyle]="{ background: selectColor(method.status) }">
{{ method.status |passfail }}
</td>
</tr>
<tr>
<td *ngIf="method.exceptionClass != null" class="table-danger">{{ method.exceptionClass }}</td>
<td *ngIf="method.exceptionClass != null" colspan="5" class="table-danger">
<button class="btn btn-dark" (click)="toggleStackTrace(index)">{{ btnText }} Stack Trace</button>
<span class="font-weight-bold ml-1">Exception Message: {{ method.exceptionMessage }}</span>
<div *ngIf="method.showStackTrace">
<p [appStacktrace]="method.stackTrace"></p>
</div>
</td>
</tr>
</tbody>
</table>
Essentially what I'm trying to do is generate a row for each test that gets run, and, if the test has a stacktrace, have a row for that as well. Is there a good, clean way to go about doing this?
Upvotes: 4
Views: 2442
Reputation: 18271
The best way to do this would be to use ng-container
.
The Angular docs describe ng-container as
a grouping element that doesn't interfere with styles or layout because Angular doesn't put it in the DOM.
This allows you to wrap your HTML in an ng-container
tag, like so:
<table class="table table-striped table-dark table-hover table-sm table-responsive" *ngIf="loaded">
<thead>
<tr>
<th>Name</th>
<th>Signature</th>
<th>Duration</th>
<th>Started At</th>
<th>Finished At</th>
<th>Status</th>
</tr>
</thead>
<tbody>
<ng-container *ngFor="let method of testResults.tests; let index = index">
<tr>
<td>{{ method.name | camelToTitle }}</td>
<td>{{ method.signature }}</td>
<td>{{ method.duration_ms }} ms</td>
<td>{{ method.startTime | dates }}</td>
<td>{{ method.finishTime | dates }}</td>
<td
[ngStyle]="{ background: selectColor(method.status) }">
{{ method.status |passfail }}
</td>
</tr>
<tr>
<td *ngIf="method.exceptionClass != null" class="table-danger">{{ method.exceptionClass }}</td>
<td *ngIf="method.exceptionClass != null" colspan="5" class="table-danger">
<button class="btn btn-dark" (click)="toggleStackTrace(index)">{{ btnText }} Stack Trace</button>
<span class="font-weight-bold ml-1">Exception Message: {{ method.exceptionMessage }}</span>
<div *ngIf="method.showStackTrace">
<p [appStacktrace]="method.stackTrace"></p>
</div>
</td>
</tr>
</ng-container>
</tbody>
</table>
The behavior of *ngFor
is maintained, but the DOM will only ever contain the expected tags (tbody
, tr
, td
, etc)
Upvotes: 8