angular2 how to use ng-template from a different file? When I place the ng-template within the same HTML where I use it works but when I move ng-template into a separate file then it won't work. Is there a way to move ng-template into its own file and use it in different html file?
info-message.html
<ng-template #messageTemplate>
Hi
</ng-template>
<ng-container *ngTemplateOutlet="messageTemplate;"></ng-container>
above is working fine because ng-template and the usage is in same file
message-template.html
<ng-template #messageTemplate>
Hi
</ng-template>
info-message.html
<ng-container *ngTemplateOutlet="messageTemplate;"></ng-container>
This is not working. Is there a way to use "messageTemplate" which is in a separate file inside another html(Eg: info-message.html)
Thanks in advance.
This behaviour can be achieved via a 'portal'. This is a useful and fairly common pattern in Angular applications. For example you may have a global sidebar outlet living near the top app level and then child components may specify a local <ng-template/>
, as part of their overall template, to be rendered at this location.
Note that while the <ng-template/>
may be defined outside of the file where the desired outlet is defined, it is still necessary to place the <ng-template/>
inside the template of some component. This can be a minimalist component which is only responsible for wrapping the <ng-template/>
, however it could equally be a complicated component where the <ng-template/>
of interest only plays a minor part.
This code illustrates one possible basic implementation of a portal.
@Directive({
selector: '[appPortal]'
})
export class PortalDirective implements AfterViewInit {
@Input() outlet: string;
constructor(private portalService: PortalService, private templateRef: TemplateRef<any>) {}
ngAfterViewInit(): void {
const outlet: PortalOutletDirective = this.portalService.outlets[this.outlet];
outlet.viewContainerRef.clear();
outlet.viewContainerRef.createEmbeddedView(this.templateRef);
}
}
@Directive({
selector: '[appPortalOutlet]'
})
export class PortalOutletDirective implements OnInit {
@Input() appPortalOutlet: string;
constructor(private portalService: PortalService, public viewContainerRef: ViewContainerRef) {}
ngOnInit(): void {
this.portalService.registerOutlet(this);
}
}
@Injectable({
providedIn: 'root'
})
export class PortalService {
outlets = new Map<string, PortalOutletDirective>();
registerOutlet(outlet: PortalOutletDirective) {
this.outlets[outlet.appPortalOutlet] = outlet;
}
}
It works using three parts:
<ng-template/>
and takes as input the name of the outlet at which the content should be rendered.<ng-container/>
, and defines the outlet.This may seem like a lot of work for something quite simple but once this plumbing is in place it is easy to (re)use.
<div class="container">
<div class="row">
<div class="col-6">
<app-foo></app-foo>
</div>
<div class="col-6">
<ng-container [appPortalOutlet]="'RightPanel'"></ng-container>
</div>
</div>
</div>
// foo.component.html
<h1>Foo</h1>
<ng-template appPortal [outlet]="'RightPanel'">
<h1>RIGHT</h1>
</ng-template>
In general it's not a great idea to reinvent the wheel though when there are already well-tested, documented and stable implementations available. The Angular CDK provides such an implementation and I'd advise to use that one rather than your own in practice.