Roy
Roy

Reputation: 8069

Display clickable list items based on condition in Angular

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

Answers (3)

KrekkieD
KrekkieD

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

dev-dan
dev-dan

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

pjlamb12
pjlamb12

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

Related Questions