Reusable Component Pieces with ngTemplate Outlet
Tags: angular
Published: 2022-12-09 07:11:49

---
title: Reusable Component Pieces with ngTemplate Outlet
published: true
date: 2022-07-25 00:00:00 UTC
tags: angular
canonical_url: https://www.prestonlamb.com/blog/reusable-component-pieces-with-ngtemplate-outlet
---

## tldr;

`ng-template`, `ng-container`, and `ngTemplateOutlet` provide the ability to reuse content inside an Angular component. Understanding what’s going on and how to use these methods allows a developer to build what they want without needing to duplicate parts of the template.

## Explanation of the Problem

Have you ever needed to use `*ngIf` in your template, and if the expression evaluates to false use a backup template? On top of that, maybe both situations (if the expression is true or false) require the same layout inside of the element that has the `*ngIf` directive on it.

I recently had came across this situation. We had some data that was being output in a list either had a link that stayed internal to the app or opened a link in a new tab external to the application. The content inside the links, however, always had the same layout. I didn’t want to duplicate the inner content, and luckily Angular provides the ability to accomplish this. The method includes using `ng-template`, `ng-container`, and `ngTemplateOutlet`.

Before getting to the end solution, let’s look at an example of the data and how it’s output without using `ng-template`.

```ts
export class ListComponent {
  links = [
    { internal: true, display: "Home", url: "/" },
    { internal: false, display: "External", url: "https://test.com" },
  ];
}
```
```html
<ul>
  <li *ngFor="let link of links">
    <a *ngIf="link.internal" [routerLink]="link.url">
      <img src="/assets/icon.svg" alt="">
      <span>{{ link.display }}</span>
    </a>
    <a *ngIf="!link.internal" [attr.href]="link.url">
      <img src="/assets/icon.svg" alt="">
      <span>{{ link.display }}</span>
    </a>
  </li>
</ul>
```

In this method, we have two `*ngIf` directives on two separate `a` tags, and the inner content of the link is the same in both cases. If that inner content changes, we have to make the change to both places or else there are inconsistencies in the layout. This is not ideal; we are repeating ourselves and there are multiple locations where we can introduce bugs.

## Solution

Angular provides a much more elegant solution to our problem. Our TypeScript code from above will remain the same, but we can have a lot less duplication in the HTML by using a couple special Angular tags. First I’ll show the HTML, and then we’ll discuss the different pieces.

```html
<ul>
  <li *ngFor="let link of links">
    <a *ngIf="link.internal; else externalLink" [routerLink]="link.url">
      <ng-container
        [ngTemplateOutlet]="linkLayout"
        [ngTemplateOutletContext]="{ link: link }"
      ></ng-container>
    </a>
    <ng-template #externalLink>
      <a [attr.href]="link.url">
        <ng-container
          [ngTemplateOutlet]="linkLayout"
          [ngTemplateOutletContext]="{ link: link }"
        ></ng-container>
      </a>
    </ng-template>
  </li>
</ul>

<ng-template #linkLayout let-link="link">
  <img src="/assets/icon.svg" alt="" />
  <span>{{ link.display }}</span>
</ng-template>
```

There’s a lot going on here, so let’s break it down. We’ll start with the `*ngIf` on the `a` tag.

```html
<a *ngIf="link.internal; else externalLink" [routerLink]="link.url">
  <ng-container
    [ngTemplateOutlet]="linkLayout"
    [ngTemplateOutletContext]="{ link: link }"
  ></ng-container>
</a>
<ng-template #externalLink>
  <a [attr.href]="link.url">
    <ng-container
      [ngTemplateOutlet]="linkLayout"
      [ngTemplateOutletContext]="{ link: link }"
    ></ng-container>
  </a>
</ng-template>
```

The `*ngIf` directive has an expression listed: if `link.internal` evaluates to `true`, then show this `a` tag. If it’s false, output the `ng-template` tag that’s referenced by the `externalLink` local template variable. This is how we can either navigate to a new route that’s part of the Angular app or external to the app. This is a method that can be used in any situation where your `*ngIf` needs an `else` clause.

Next up, let’s look at what’s going on with the `ng-container` inside both of the `a` tags in the example.

```html
<ng-container
  [ngTemplateOutlet]="linkLayout"
  [ngTemplateOutletContext]="{ link: link }"
></ng-container>
```

You can read more about the `ng-container` element in the [Angular docs](https://angular.io/api/core/ng-container), but at a high level it’s a special element that can hold structural directives without adding new elements to the DOM. It can also be used in situations like this, with the `ngTemplateOutlet` structural directive. The `ngTemplateOutlet` directive determines what `ng-template` will be output on the page inside the `ng-container`. The context for the template, or needed variables, can be declared with the `ngTemplateOutletContext` directive. In the above case, we are saying that the `#linkLayout` content should be output on the page, and that there should be a variable called `link` that will be used inside the `ng-template`. The contents of the ng-template tag can be placed anywhere in the HTML file, though I’ve placed them at the bottom in this example.

```html
<ng-template #linkLayout let-link="link">
  <img src="/assets/icon.svg" alt="" />
  <span>{{ link.display }}</span>
</ng-template>
```

This template will be output on the screen anywhere it’s referenced. In this case, inside both `a` tags. If we want to change the layout inside the `a` tags, we need only change this one spot in the HTML. Everything will be kept in sync, and we have fewer possibilities of inconsistencies in the component.

There is one alternate way of declaring the `ngTemplateOutlet`. The result will be the same as the method shown above.

```html
<ng-container
  *ngTemplateOutlet="linkLayout; context: { link: link }"
></ng-container>
```

## Conclusion

The above method limits the duplication to a certain degree, especially for the content inside the `a` tag. The example here is a fairly simple layout. This method becomes more useful when the internal layout becomes more complicated. That inner layout is defined once and reused in multiple locations. You could create a new component if you wanted, but that can introduce complexity sometimes as well.

Knowing how to use `ng-template`, `ng-container`, and `ngTemplateOutlet` can allow you to effectively output the content of a component with minimal duplication