Search code examples
angulartemplatesangular-ng-ifangular-template

Looping ng-template is showing wrong template when using *ngIf and ng-content


I have a simple data list component which as inputs one can pass the data to be shown, set a header for the list as well as a custom template for the list items.

All this works fine, however when I use an *ngIf inside the header for some reason the template of the list items is not the same as the one I'm passing. I think it might be an issue with the @ContentChild but I'm not sure what the issue is.

Data List Component

@Component({
    selector: 'data-list',
    template: `
        <ng-content select="[data-list-header]"></ng-content>
        <div>
            <ng-template *ngFor="let item of itemsData" let-item [ngForOf]="[item]" [ngForTemplate]="itemTemplate">
            </ng-template>
        </div>
    `
})

export class DataListComponent {
    @Input() itemsData: any[];
    @ContentChild(TemplateRef) itemTemplate: TemplateRef<ElementRef>;
}

Template

<data-list [itemsData]="items">
    <div data-list-header style="border-bottom: 2px solid grey;">
        <h1>Persons List</h1>
        <h2>Count: {{ items.length }}</h2>
    </div>
    <ng-template let-item>
        <div>
            <h4>{{ item.name }} {{ item.surname }}</h4>
        </div>
    </ng-template>
</data-list>

Results In

enter image description here

The above templates works fine, a list of persons is shown. However once I add an *ngIf to hide the Count as an example a list of counts is displayed instead.

Template not working properly

<data-list [itemsData]="items">
    <div data-list-header style="border-bottom: 2px solid grey;">
        <h1>Persons List</h1>
        <h2 *ngIf="showCount">Count: {{ items.length }}</h2>
    </div>
    <ng-template let-item>
        <div>
            <h4>{{ item.name }} {{ item.surname }}</h4>
        </div>
    </ng-template>
</data-list>

Results In

enter image description here

This is a stackblitz of the above: https://stackblitz.com/edit/ng-template-loop-issue


Solution

  • The issue happens because when using structural directives (such *ngIf) ng-template is always created.

    Now having @ContentChild(TemplateRef) means that a query to find the first template (ng-template) is done. Therefore in this case having *ngIf on the header meant that an ng-template was automatically created which was then picked up by the query.

    To fix this issue a template reference was used so that the @ContentChild() query would be done searching for the template reference. Example: @ContentChild('itemTemplate').

    Updated code: https://stackblitz.com/edit/ng-template-loop-issue

    More Info: