In my previous article, we saw how to strictly type-check the NgTemplateOutlet
when it is used on a child component. In this article, we will see how to do this when the TemplateRef
is located inside the same template as the NgTemplateOutletDirective
We define the following template where our TemplateOutlet
directive is embedded inside the #personRef
template.
<ng-container
*ngTemplateOutlet="
personRef;
context: { $implicit: person.name, age: person.age }
">
</ng-container>
<ng-template #personRef let-name let-age="age">
{{ name }}: {{ age }}
</ng-template>
This template is working perfectly but we don't have any autocompletion or type inference.
I invite you to read my previous article on NgTemplateOutlet Type Checking and Directive Type Checking to have a better understanding over the following step.
As described in my previous article, we can create a directive with a ngTemplateContextGuard
that defines our context PersonContext
.
interface PersonContext {
$implicit: string;
age: number;
}
@Directive({
selector: 'ng-template[person]',
standalone: true,
})
export class PersonDirective {
static ngTemplateContextGuard(
dir: PersonDirective,
ctx: unknown
): ctx is PersonContext {
return true;
}
}
We apply this directive on our template and that's it, we have a correct type inference.
However what about our directive? How can we apply strict type checking by passing our context? We can see in the image below that our compiler doesn't complain when we pass wrong property to our context.
Remark: as this time of writing, ngTemplateOutlet is not strictly typed and I'm using my own directive for the following part. (You can find the code here).
This part is a bit trickier because we need to get our template reference in order to get our context.
On our previous article, @ContentChild
decorator's read
meta property was doing this part for us. Now we have to do it ourself.
To do this, we will first inject our template reference inside our directive with the correct context. And to get that reference from outside, Angular exposes the exportAs
option on the directive decorator.
exportAs
defines a name that can be used from the template.
@Directive({
selector: 'ng-template[person]',
exportAs: 'personExportAsRef', // named this way for better clarity
standalone: true,
})
export class PersonDirective {
constructor(public template: TemplateRef<PersonContext>){}
static ngTemplateContextGuard(
dir: PersonDirective,
ctx: unknown
): ctx is PersonContext {
return true;
}
}
Now we can export our directive inside our template and have access to its public internal properties.
<ng-container
*ngTemplateOutlet="
personRef.template; <!-- ref to directive TemplateRef -->
context: { $implicit: person.name, age: person.age }
">
</ng-container>
<!-- assign the directive to personRef to access internal properties -->
<ng-template #personRef="personExportAsRef" person let-name let-age="age">
{{ name }}: {{ age }}
</ng-template>
Now, the compiler is complaining if the property is of the wrong type or if one context property is missing.
NgTemplateOutlet and Directives should have no more secret for you. You can now create nice and secure application for your teammates.
If you haven't read all related articles, I invite you to do it :
- Typescript Type Predicate
- Directive Type Checking
- NgTemplateOutlet Type Checking - with @ContentChild
I hope you learned new Angular concept. If you liked it, you can find me on Medium, Twitter or Github.
👉 And if you want to accelerate your Angular learning journey, come and check out Angular challenges.
Top comments (1)
From which version of Angular it is supported ??