Rafael de Castro
Rafael de Castro

Reputation: 1098

How to add custom template to Generic list component

I have this application where several lists on diferent components are being made. The pattern for the component is always the same.

Master list items with ADD button and a REMOVE button for each item. But the item itself vary alot, and some have its own list items.

I was trying to create a shareable component (cause DRY huh? :D )

But I cant' get to my ngFor to repeat the items inside the component.

I made this Stackblitz with the work.

Here's my code:

Component being used:

<chained-item-list [items]="items" (onRemoveItem)="onRemoveItem($event)">
  <div class="card">
    <h4>Header Item</h4>
    <small>22/03/2022</small>
    <p>Lorem ipsum dolor amet.</p>
  </div>
</chained-item-list>

Component .HTML file

<div class="list-header">
  <h3>CHAINED LIST</h3>

  <button (click)="addNewItem()" title="Add Item" type="button" class="btn-add">
    Add New
  </button>
</div>
<br />

<ul>
  <ng-container *ngFor="let item of items; let indexItem = index">
    <li>
      <ng-content></ng-content>

      <button
        (click)="removeItem(indexItem)"
        title="Remove"
        type="button"
        class="btn-remove"
      >
        X
      </button>
    </li>
  </ng-container>
</ul>

Component .TS file

export class ChainedItemListComponent {
  @Input() items: any[];
  @Output() onRemoveItem: EventEmitter<any> = new EventEmitter();


  constructor() {}

  addNewItem() {
    this.items.push(Math.floor(Math.floor(Math.random() * 100)));
  }

  removeItem(indexItem: number) {
    try {
      this.onRemoveItem.emit(this.items.splice(indexItem, 1)[0]);
    } catch (e) {
      this.onRemoveItem.emit(e);
      console.log(e);
    }
  }
}

Upvotes: 0

Views: 1472

Answers (2)

Krunal Limbad
Krunal Limbad

Reputation: 1573

Let's start with defining template.

<chained-item-list [items]="items" (onRemoveItem)="onRemoveItem($event)">
  <ng-template #contentAccessTpl="">
    <div class="card">
      <h4>Header Item</h4>
      <small>22/03/2022</small>
      <p>Lorem ipsum dolor amet.</p>
    </div>
  </ng-template>
</chained-item-list>

For safer side add condition. by assigning the Id it will make it easy to use multiple templates.

<ul>
  <ng-container *ngFor="let item of items; let indexItem = index">
    <li>
      <ng-container *ngIf="contentAccessTpl">
        <ng-container *ngTemplateOutlet="contentAccessTpl"> </ng-container>
      </ng-container>

      <button
        (click)="removeItem(indexItem)"
        title="Remove"
        type="button"
        class="btn-remove"
      >
        X
      </button>
    </li>
  </ng-container>
</ul>

Here's forked stackblitz.

Upvotes: 1

Justwell Solets
Justwell Solets

Reputation: 171

It's very simple. You need to update you component.

Change the way of passing a template, use ng-template.

<chained-item-list [items]="items" (onRemoveItem)="onRemoveItem($event)">
  <!-- pass template using ng-template tag -->
  <ng-template>
    <div class="card">
      <h4>Header Item</h4>
      <small>22/03/2022</small>
      <p>Lorem ipsum dolor amet.</p>
    </div>
  </ng-template>
</chained-item-list>

Add this line to your component ts file.

@ContentChild(TemplateRef) customTemplateRef: TemplateRef<any>;

Now pass this customTemplateRef to the ng-content tag.

<ul>
  <ng-container *ngFor="let item of items; let indexItem = index">
    <li>
      <!-- update this line -->
      <ng-content *ngTemplateOutlet="customTemplateRef"></ng-content>

      <button
        (click)="removeItem(indexItem)"
        title="Remove"
        type="button"
        class="btn-remove"
      >
        X
      </button>
    </li>
  </ng-container>
</ul>

You're done. Enjoy!

Upvotes: 3

Related Questions