4

I have a recursive data structure (recursive via children property) as below:

export interface IExecutableLog {
    round: number;
    log: string;
}

export interface IExecutableResult {
    uid: string;
    name: string;
    desc: string;
    status: string;
    passedRounds: number;
    totalRound: number;
    children?: IExecutableResult[];
    statementType?: string;
    logs?: IExecutableLog[];
}

export interface ISummary {
    title: string;
    jobName: string;
    timestamp: Date;
    tester: string;
    result: string;
    executionId: string;
    testJobId: string;
    resultDetails: IExecutableResult;
}

And I want to display data of Isummary type from backend.

I tried define a component as below:

// pats-report-element.component.ts
import { Component, Input, ViewEncapsulation } from '@angular/core';
import { IExecutableResult } from '../pats-report.interface';

@Component({
    selector: 'pats-report-element',
    templateUrl: 'app/report/basic/pats-report-element.html',
    encapsulation: ViewEncapsulation.None,
})
export class  PatsReportElementComponent {
    @Input()
    public data: IExecutableResult;
}
// app/report/basic/pats-report-element.html
<tr>
    <td>{{data?.name}}</td>
    <td>{{data?.status}}</td>
    <td>{{data?.rounds}}</td>
    <td>{{data?.passedRounds}}</td>
    <td>{{data?.rounds > 0 ? (data.passedRounds / data.rounds) * 100 + "%" : "NA"}}</td>
    <td>{{data?.timestamp | date:"y-MM-dd HH:mm:ss"}}</td>
</tr>

<template [ngIf]="data && data.children">
    <template ngFor let-item [ngForOf]="data.children" let-i="index">
        <pats-report-element [data]="item"></pats-report-element>
    </template>
</template>

But the rendered DOM will include the host element 'pats-report-element' which is not valid inside a <table></table>tag. The DOM is as below: enter image description here

I checked the angular2 doc, and seems attribute-directives is the right choice. But I cannot find a way to use template with attribute-directives just like what I do in PatsReportElementComponent.

So what's the correct way to achieve my goal?

[Update 1]

Tried @Günter Zöchbauer's suggestion, still not resolve my issue. The rendered DOM is still not as expected (the <tr></tr> still not being flattened).

enter image description here

3

4 Answers 4

1

You can use an attribute selector

@Component({
  selector: '[pats-report-element]',

then you can add your element like

<tr pats-report-element [data]="item"></tr>

There is no way to get an element rendered without it's selector element.

Sign up to request clarification or add additional context in comments.

4 Comments

Thanks for your answer! Can we use attribute selector with @Component? I only saw using attribute selector with @Directive. Would you please point where is the document? I've updated my code according to your answer, but there is exception: ` Template parse errors: Can't bind to 'data' since it isn't a known property of 'pats-report-element'. 1. If 'pats-report-element' is an Angular component and it has 'data' input, then verify that it is part of this module. ...`
Works the same with @Component(). A component is a directive ;-) just with an additional view.
Got exception: zone.js:461 Unhandled Promise rejection: Template parse errors: Can't bind to 'data' since it isn't a known property of 'pats-report-element'. 1. If 'pats-report-element' is an Angular component and it has 'data' input, then verify that it is part of this module. 2. If 'pats-report-element' is a Web Component then add "CUSTOM_ELEMENTS_SCHEMA" to the '@NgModule.schema' of this component to suppress this message. Could you see anything wrong?
Fix the exception and tried your approach, but still not get what I want. Please see my update on the origin post. Thanks again!
1

After many efforts, I still failed to get it done with native angular2. And I ended up with a 3rd party library--ag-grid-ng2. It works well except it's overkill for my requirements.

Comments

0

I am not sure if it will solve your problem, in the component you are referring to the same component recursively in the template, you should self-reference it in your directive.

// pats-report-element.component.ts import { Component, Input, ViewEncapsulation } from '@angular/core'; import { IExecutableResult } from '../pats-report.interface';

@Component({
    selector: 'pats-report-element',
    directives: [forwardRef(() => PatsReportElementComponent )], // <<<<<< change
    templateUrl: 'app/report/basic/pats-report-element.html',
    encapsulation: ViewEncapsulation.None,
})
export class  PatsReportElementComponent {
    @Input()
    public data: IExecutableResult;
}
// app/report/basic/pats-report-element.html
<tr>
    <td>{{data?.name}}</td>
    <td>{{data?.status}}</td>
    <td>{{data?.rounds}}</td>
    <td>{{data?.passedRounds}}</td>
    <td>{{data?.rounds > 0 ? (data.passedRounds / data.rounds) * 100 + "%" : "NA"}}</td>
    <td>{{data?.timestamp | date:"y-MM-dd HH:mm:ss"}}</td>
</tr>

<template [ngIf]="data && data.children">
    <template ngFor let-item [ngForOf]="data.children" let-i="index">
        <pats-report-element [data]="item"></pats-report-element>
    </template>
</template>

See if this works for you.

Comments

0

Recursive tables

You can put another <table> for level's childes inside <my-component-selector>. Just do it after current level's table tag closed:

<table>
    <tbody>
        <tr>
            <td>{{level.id}}</td>
            <td>{{level.title}}</td>
        </tr>
    </tbody>
</table>
<div *ngFor="let item of level.childLevels">
    <my-component-selector [level]="item">
         <!-- Here will be inserted another instance of this HTML file with itself table and itself recursion -->
    </my-component-selector>
</div>

Where you have component for each hierarhy level like this:

@Component({
    selector: 'my-component-selector',
    templateUrl: './my-recursive.component.html'
})
export class MyRecursiveComponent implements OnInit {

    @Input() level: LevelItem;

    onInit() {
        if (!level) {
             loadRootLevelByNetwork();
        }
    }

}

Here level variable are set from outer component by [level]="item" in HTML or loaded by network (for root component).

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.