Introduction
The single responsibility principle means that each part of an application should have only one purpose. Following this principle can make your Angular application easier to test and develop.
In Angular, useNgTemplateOutlet
Instead of creating specific components, it can make it easy for components to be modified into various use cases without modifying the component itself.
In this article, you will accept an existing component and rewrite it to useNgTemplateOutlet
。
Prerequisites
To complete this tutorial, you need:
- Installed locally, you can follow "How to Install and Create a Local Development Environment".
- Some familiarity about setting up Angular projects.
This tutorial has used Node v16.6.2.npm
v7.20.6 and@angular/core
v12.2.0 for verification.
Step 1 – Building CardOrListViewComponent
considerCardOrListViewComponent
, it is based on itsmode
exist'card'
or'list'
Display in formatitems
。
It consists of aFile composition:
import { Component, Input } from '@angular/core'; @Component({ selector: 'card-or-list-view', templateUrl: './' }) export class CardOrListViewComponent { @Input() items: { header: string, content: string }[] = []; @Input() mode: string = 'card'; }
And onetemplate:
<ng-container [ngSwitch]="mode"> <ng-container *ngSwitchCase="'card'"> <div *ngFor="let item of items"> <h1>{{}}</h1> <p>{{}}</p> </div> </ng-container> <ul *ngSwitchCase="'list'"> <li *ngFor="let item of items"> {{}}: {{}} </li> </ul> </ng-container>
Here is an example of using this component:
import { Component } from '@angular/core'; @Component({ template: ` <card-or-list-view [items]="items" [mode]="mode"> </card-or-list-view> ` }) export class UsageExample { mode = 'list'; items = [ { header: 'Creating Reuseable Components with NgTemplateOutlet in Angular', content: 'The single responsibility principle...' } // ... more items ]; }
The component has no single responsibility and is not flexible enough. It needs to track itmode
And know how tocard
andlist
Show in viewitems
. It can only display withheader
andcontent
ofitems
。
Let's change this by breaking the component into separate views using templates.
Step 2 – Understand ng-template and NgTemplateOutlet
To makeCardOrListViewComponent
Able to display any type ofitems
, we need to tell it how to display them. We can do this by giving it a template, which can be used to generateitems
。
The template will be used<ng-template>
and fromTemplateRefs
CreatedEmbeddedViewRefs
。EmbeddedViewRefs
A Angular view that represents its own context is the smallest basic building block.
Angular provides a way to use this concept of generating views from templates, i.e.NgTemplateOutlet
。
NgTemplateOutlet
is an instruction, it accepts aTemplateRef
and context, and generate aEmbeddedViewRef
. Can be passedlet-{{templateVariableName}}="contextProperty"
Properties access the context on the template to create variables that the template can use. If the context attribute name is not provided, it will select$implicit
property.
Here is an example:
import { Component } from '@angular/core'; @Component({ template: ` <ng-container *ngTemplateOutlet="templateRef; context: exampleContext"></ng-container> <ng-template #templateRef let-default let-other="aContextProperty"> <div> $implicit = '{{default}}' aContextProperty = '{{other}}' </div> </ng-template> ` }) export class NgTemplateOutletExample { exampleContext = { $implicit: 'default context property when none specified', aContextProperty: 'a context property' }; }
Here is the output of the example:
<div> $implicit = 'default context property when none specified' aContextProperty = 'a context property' </div>
default
andother
Variables arelet-default
andlet-other="aContextProperty"
Properties are provided.
Step 3 – Refactor CardOrListViewComponent
In order to makeCardOrListViewComponent
More flexible and allows it to display any type ofitems
, we will create two structured directives as templates. These templates will be used for card and list items respectively.
This is:
import { Directive } from '@angular/core'; @Directive({ selector: '[cardItem]' }) export class CardItemDirective { constructor() { } }
This is:
import { Directive } from '@angular/core'; @Directive({ selector: '[listItem]' }) export class ListItemDirective { constructor() { } }
CardOrListViewComponent
Will importCardItemDirective
andListItemDirective
:
import { Component, ContentChild, Input, TemplateRef } from '@angular/core'; import { CardItemDirective } from './'; import { ListItemDirective } from './'; @Component({ selector: 'card-or-list-view', templateUrl: './' }) export class CardOrListViewComponent { @Input() items: { header: string, content: string }[] = []; @Input() mode: string = 'card'; @ContentChild(CardItemDirective, {read: TemplateRef}) cardItemTemplate: any; @ContentChild(ListItemDirective, {read: TemplateRef}) listItemTemplate: any; }
This code will read our structured instruction asTemplateRefs
。
<ng-container [ngSwitch]="mode"> <ng-container *ngSwitchCase="'card'"> <ng-container *ngFor="let item of items"> <ng-container *ngTemplateOutlet="cardItemTemplate"></ng-container> </ng-container> </ng-container> <ul *ngSwitchCase="'list'"> <li *ngFor="let item of items"> <ng-container *ngTemplateOutlet="listItemTemplate"></ng-container> </li> </ul> </ng-container>
Here is an example of using this component:
import { Component } from '@angular/core'; @Component({ template: ` <card-or-list-view [items]="items" [mode]="mode"> <div *cardItem> Static card template </div> <li *listItem> Static list template </li> </card-or-list-view> ` }) export class UsageExample { mode = 'list'; items = [ { header: 'Create reusable components in Angular using NgTemplateOutlet', content: 'Single Responsibility Principle...' } // ... More items ]; }
Through these changes,CardOrListViewComponent
It is now possible to display any type of item in a card or list form based on the provided template. Currently, the template is static.
The last thing we need to do is to make the template dynamic by giving them context:
<ng-container [ngSwitch]="mode"> <ng-container *ngSwitchCase="'card'"> <ng-container *ngFor="let item of items"> <ng-container *ngTemplateOutlet="cardItemTemplate; context: {$implicit: item}"></ng-container> </ng-container> </ng-container> <ul *ngSwitchCase="'list'"> <li *ngFor="let item of items"> <ng-container *ngTemplateOutlet="listItemTemplate; context: {$implicit: item}"></ng-container> </li> </ul> </ng-container>
Here is an example of using this component:
import { Component } from '@angular/core'; @Component({ template: ` <card-or-list-view [items]="items" [mode]="mode"> <div *cardItem="let item"> <h1>{{}}</h1> <p>{{}}</p> </div> <li *listItem="let item"> {{}}: {{}} </li> </card-or-list-view> ` }) export class UsageExample { mode = 'list'; items = [ { header: 'Create reusable components in Angular using NgTemplateOutlet', content: 'Single Responsibility Principle...' } // ... More items ]; }
Interestingly, we used asterisk prefix and microgrammar to implement syntax sugar. This is the same as the following code:
<ng-template cardItem let-item> <div> <h1>{{}}</h1> <p>{{}}</p> </div> </ng-template>
That's it! We have the original functionality, but now we can display whatever we want by modifying the template, andCardOrListViewComponent
There is less responsibility. We can add more to the item context, such asngFor
offirst
orlast
, or display a completely different type ofitems
。
in conclusion
In this article, you rewrite an existing component to useNgTemplateOutlet
。
If you want to learn more about Angular, check out our Angular topic page for relevant exercises and programming projects.
The above is the detailed process steps for creating reusable components using NgTemplateOutlet in Angular. For more information about reusable components of Angular NgTemplateOutlet, please follow my other related articles!