Reputation: 8069
Let's say I have this list of people I want to display. Below you'll see the HTML for this iteration in a *ngFor
loop. You can view this StackBlitz to check out the full example.
<mat-list role="list">
<mat-list-item *ngFor="let name of names" role="listitem">
<mat-icon mat-list-icon>person</mat-icon>
<h4 mat-line>{{name.firstName}}</h4>
</mat-list-item>
</mat-list>
In some cases the list should be displayed as a linked list, since the list of people then link to other webpages. What I could do to achieve this is to write an *ngIf
that checks if it should be a linked list or a normal list, as written below.
<div *ngIf="isNormalList; else isLinkedList">
<h3>Normal list with items</h3>
<mat-list role="list">
<mat-list-item *ngFor="let name of names" role="listitem">
<mat-icon mat-list-icon>person</mat-icon>
<h4 mat-line>{{name.firstName}}</h4>
</mat-list-item>
</mat-list>
</div>
<ng-template #isLinkedList>
<h3>List with clickable items</h3>
<mat-list role="list">
<a mat-list-item href="#" *ngFor="let name of names" role="listitem">
<mat-icon mat-list-icon>person</mat-icon>
<h4 mat-line>{{name.firstName}}</h4>
</a>
</mat-list>
</ng-template>
However, solving it this way seems like a lot of duplicate code. I could also write an *ngIf
for the inner part of the list item, but that is more or less the same and ends up in duplicate code as well.
Is there a way to only add the a
element conditionally within this setup?
Upvotes: 3
Views: 2597
Reputation: 1017
Something like this should work. I think you can ease the context
bit when using $implicit
to make it shorter, but not sure how exactly, you can check the Angular docs.
On a side note, I don't think you need to specify the role
attributes, Material should provide these for you.
<div>
<mat-list role="list">
<ng-container *ngIf="isNormalList; else isLinkedList">
<mat-list-item *ngFor="let name of names" role="listitem">
<ng-container *ngTemplateOutlet="listItem; context: { $implicit: name }"></ng-container>
</mat-list-item>
</ng-container>
<ng-template #isLinkedList>
<a mat-list-item href="#" *ngFor="let name of names" role="listitem">
<ng-container *ngTemplateOutlet="listItem; context: { $implicit: name }"></ng-container>
</a>
</ng-template>
</mat-list>
</div>
<ng-template #listItem let-name>
<mat-icon mat-list-icon>person</mat-icon>
<h4 mat-line>{{name.firstName}}</h4>
</ng-template>
Upvotes: 2
Reputation: 6283
I think there is balance to be found here between saving lines and remaining readable, I think it could get over complicated trying to ensure absolutely no code repetitions.
Simple and readable.
<div>
<h3>List with clickable items</h3>
<mat-list role="list">
<div *ngFor="let name of names">
<a *ngIf="name.link" mat-list-item href="#" role="listitem">
<mat-icon mat-list-icon>person</mat-icon>
<h4 mat-line>{{name.firstName}}</h4>
</a>
<div *ngIf="!name.link">
<mat-icon mat-list-icon>person</mat-icon>
<h4 mat-line>{{name.firstName}}</h4>
</div>
</div>
</mat-list>
</div>
This will make the following repeated, with the addition of two new <div>
tags.
<mat-icon mat-list-icon>person</mat-icon>
<h4 mat-line>{{name.firstName}}</h4>
Which would be the least repetitive way I could think of, without doing something weird / hacky like stretching the <a>
over mat-icon
and h4
with CSS if it exists. Which isn't pleasant or particularly readable.
Upvotes: 1
Reputation: 2422
You can put the *ngFor
on an ng-template
tag:
<ng-template ngFor [ngForOf]="items" let-item>
<div *ngIf="!item.link" class="card">
<h4>Card Info:</h4>
<p>ID: {{ item.id }}</p>
<p>
Title: {{ item.title }}
</p>
</div>
<a *ngIf="item.link" href="{{ item.link }}" class="card">
<h4>Card Info:</h4>
<p>ID: {{ item.id }}</p>
<p>
Title: {{ item.title }}
</p>
</a>
</ng-template>
Check out this Stackblitz for an example.
Upvotes: 0