While playing with the concept of ng-template (html to to render), ng-container (work with ng-template for rendering) and ng-content (content projection), I'm having trouble to make use of $implicit value in a particular situation.
When the context is provided within *ngTemplateOutlet using a ng-container and content projection,the implicit value is not present.
Cannot figure out why, any idea ?
See bellow the code :
<!-- app.component.html -->
<ng-template #inputTemplate let-labelDescription="inputLabel" let-defaultTagValue="inputValue">
<label for="action">{{labelDescription}}</label><input id="idAction" value="{{defaultTagValue}}">
</ng-template>
<ng-template #buttonTemplate let-labelDescription="buttonLabel" let-defaultTagValue="buttonValue">
<label for="action">label: {{labelDescription}}</label><h1><button id="idAction">my value : {{defaultTagValue}}</button></h1>
</ng-template>
<testing-componenet [id]="0" [description]="'send template button'">
<ng-container *ngTemplateOutlet="buttonTemplate; context: { buttonLabel:'LABEL_BUTTON', $implicit: '..data..'}"></ng-container>
</testing-componenet>
// testing-component.component.ts
import { Component, Input } from '@angular/core';
@Component({
selector: 'testing-component',
standalone: true,
imports: [],
template: "<p> {{id}} </p><p> {{description}} </p> <ng-content></ng-content>"
})
export class TestingComponentComponent {
@Input( {required:true} )
id!: number ;
@Input( {required:true} )
description!:string;
}
Result :


You have 2 options.
Since your second template expects a
buttonLabelandbuttonValuetemplate variableYou should use the following
ngTemplateOutletsnippetOn the other hand, you could use
$implicitif you wanted tobut then you have to pass in the following template
Making it more awesome
At the moment you're using
<ng-content>which projects the<ng-container>and templateoutletdirective, and you're kinda lucky this works. A more slick way to do this is by writing a new directiveSo now you can use your component like this:
To take things even further you can write it using the structural directive notation
Or you could also use a default template using the coalescing operator:
So the conclusion is that you have 2 options. You can either use
<ng-content select="[slot1]">or you can use structural directives, assign the template to the main generic component, and project it while passing information from inside the generic component to wherever you're using it. I always tend to opt for the template approach, since it's more flexible and easier to pass data to the parent. I use it all the time. Here's an example component and here's how to use it.Adding a template context guard
If you would take a look now at where you use your structural directive, you'd see that the template variables you receive have the
anytypeYou can solve this by adding a
ngTemplateContextGuardstatic field to your directive (DOCS):Result:
StackBlitz, but they don't show tooltips in templates.