Reusable HTML in Angular using NgTemplateOutlet
Tags: angular, javascript, html
Published: 2020-02-14 22:16:48

When creating an application, you might find yourself reusing the same HTML. One way to get rid of this could be to make use of components. Even tho I love using components for this, Angular allows us to reuse HTML without introducing components as well.

Recently I find myself using this as I wanted to limit the amount of refactoring in the current PR (so I didn't want to move parts of the HTML and code into a component) while I still wanted to introduce reusability in the HTML.

Take a look at the following example:
```html
    <h1>Movies</h1>
    <div *ngFor="let movie of movies">
      <span>{{ movie.name }}</span>
    	<p>{{ movie.description }}</p>
    	<button (click)="onClick(movie)">Click Me</button>
    </div>
    
    <h1>Favorite Movies</h1>
    <div *ngFor="let movie of favoriteMovies">
      <span>{{ movie.name }}</span>
    	<p>{{ movie.description }}</p>
    	<button (click)="onClick(movie)">Click Me</button>
    </div>
```
As mentioned, to limit duplication and introduce reusability we could move the content of our `ngFor` into its own component, add the appropriate input and outputs and set up the bindings appropriately.

Apart from using a Component, we can also make use of Angular's NgTemplateOutlet. I'll generally recommend using a component. However, if you find your self in a situation where the HTML you wanna reuse is so big and bound to the current component in so many ways, using NgTemplateOutlet might be a first step into making the HTML reusable. Another reason to use NgTemplateOutlet could also be when the HTML is so simple, that introducing a separate component might not be making things easier to grasp.
```html
    <ng-template #itemTemplate>
      <span>{{ item.name }}</span>
    	<p>{{ item.description }}</p>
    	<button (click)="onClick(item)">Click Me</button>
    </ng-template>
```
Moving the content of our `ngFor` into an `ng-template` is pretty straightforward. However, as we want to render this template several times, each time for a different item, we'll need to declare some kind of context for the ng-template so that angular knows how to bind the data. In our case, we'll be adding `let-item` to the ng-template.
```html
    <ng-template #itemTemplate let-item>
      <span>{{ item.name }}</span>
    	<p>{{ item.description }}</p>
    	<button (click)="onClick(item)">Click Me</button>
    </ng-template>
```
Using this template can be done by using the `ngTemplateOutlet` directive on an `ng-container` component.
```html
    <ng-container [ngTemplateOutlet]="itemTemplate">
    </ng-container>
```
We can add a context object to ngTemplateOutlet which allows us to provide it with a value to use as the item we have defined on our ng-template.

A context object should be an object, the object's keys will be available for binding by the local template let declarations. Using the key $implicit in the context object will set its value as default.

What this means is that we can either use `let-item="movie"` and provide the context as an object containing a `movie` property: `[ngTemplateOutletContext]="{ movie: movie }"` or use `let-item` and `[ngTemplateOutletContext]="{ $implicit: movie }"`. Both of them will make the movie available as  `item` inside the template.
```html
    <ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: movie }">
    </ng-container>
```
Putting everything together, we now should be able to refactor our HTML to look like this:
```html
    <h1>Movies</h1>
    <div *ngFor="let movie of movies">
      <ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: movie }">
    	</ng-container>
    </div>
    
    <h1>Favorite Movies</h1>
    <div *ngFor="let movie of favoriteMovies">
      <ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: movie }">
    	</ng-container>
    </div>
    
    <ng-template let-item>
      <span>{{ item.name }}</span>
    	<p>{{ item.description }}</p>
    	<button (click)="onClick(item)">Click Me</button>
    </ng-template>
```
We can even add another ng-template to reuse the list. For demonstration purposes, we'll not use the `$implicit` property on the NgTemplateOutlet's context in this case. Instead, we'll explicitly assign our value to a property, other than $implicit, and use that same property in `let-list="list"`, where the right side of the assignment refers to the context property while the left side is defining a variable usable in the ng-template.
```html
    <h1>Movies</h1>
    <ng-container [ngTemplateOutlet]="listTemplate" [ngTemplateOutletContext]="{ list: movies }">
    </ng-container>
    
    <h1>Favorite Movies</h1>
    <ng-container [ngTemplateOutlet]="listTemplate" [ngTemplateOutletContext]="{ list: favoriteMovies }">
    </ng-container>
    
    <ng-template #listTemplate let-list="list">
      <div *ngFor="let item of list">
    	  <ng-container [ngTemplateOutlet]="itemTemplate" [ngTemplateOutletContext]="{ $implicit: item }">
    		</ng-container>
    	</div>
    </ng-template>
    
    <ng-template #itemTemplate let-item>
      <span>{{ item.name }}</span>
    	<p>{{ item.description }}</p>
    	<button (click)="onClick(item)">Click Me</button>
    </ng-template>
```
**Note:** The ngTemplateOutlet directive can also be used as a structural directive:
```html
<ng-container *ngTemplateOutlet="itemTemplate; context: { $implicit: item }">
</ng-container>
```

## Conclusion

Even tho using Components is still the most ideal way in Angular to introduce reusable blocks, Angular's ngTemplateOutlet allows for a way to make use of reusable blocks of HTML without introducing new components.

You can find a running example at: [https://stackblitz.com/edit/angular-fkqhbz](https://stackblitz.com/edit/angular-fkqhbz?file=src%2Fapp%2Fapp.component.ts)