I am trying to get TemplateRef
for ng-template
and input
parameters.
app.component.html:
<my-parent>
<ng-template my-directive title="Title1" let-dataItem="dataItem">
<div>{{dataItem|json}}</div>
</ng-template>
</my-parent>
Problem is I can get only TemplateRef
(title is undefined):
my-parent.html:
<div *ngFor="let detail of details" [title]="detail.title"> //!!! detail.title is undefined
<ng-container *ngTemplateOutlet="detail;context:{dataItem: dataItem}"></ng-container>
</div>
my-parent.ts
@ContentChildren(MyDirective, { read: TemplateRef }) details: QueryList<MyDirective>;
Or I can get Input parameters:
my-parent.ts
@ContentChildren(MyDirective) details: QueryList<MyDirective>;
//in html detail.title is defined, but detail is not template and I get exception in line
<ng-container *ngTemplateOutlet="detail;context:{dataItem: dataItem}"></ng-container>
Exception:
TypeError: templateRef.createEmbeddedView is not a function
I think I need this version of ContentChildren:
@ContentChildren(MyDirective, { read: TemplateRef }) details: QueryList<MyDirective>;
but how can I get MyDirective
input parameter (title
in this case)?
I know I can use both:
@ContentChildren(MyDirective, { read: TemplateRef }) detailsRefs: QueryList<MyDirective>;
@ContentChildren(MyDirective) detailsInputs: QueryList<MyDirective>;
And then merge it into new array:
public ngAfterViewInit(): void
{
const refs = this.detailsRefs.toArray();
const inputs = this.detailsInputs.toArray();
for (let i = 0; i < inputs.length; i++)
this.details.push({templateRef: refs[i], inputs: inputs[i]});
}
But sure there is a better way.
It can be done by using DI:
@Directive({
selector: '[my-directive]'
})
export class MyDirective {
@Input() title: string;
constructor(public templateRef: TemplateRef<any>) {}
}
now it should be easy to undestand that we can leverage the preceding directive like:
parent.component.ts
@ContentChildren(MyDirective) details: QueryList<MyDirective>;
parent.component.html
<ng-container *ngTemplateOutlet="detail.templateRef
^^^^^^^^^^^^^
Stackblitz Example
You can read title
with an input in my-directive
and then read it from there, for that you need to remove {read: TemplateRef}
:
@ContentChildren(MyDirective) details: QueryList<MyDirective>;
...
detail.title
As far as I remember for structural directives the input needs to be prefixed with the selector.
@Directive({ selector: 'myDirective', ...})
class MyDirective {
@Input() myDirectiveTitle:string;
}
and use it like
<ng-template myDirective title="Title1" let-dataItem="dataItem">
and read the title using
detail.myDirectiveTitle
See also https://angular.io/guide/structural-directives
This code
@ContentChildren(MyDirective, { read: TemplateRef }) details: QueryList<MyDirective>;
should cause an error because when you read TemplateRef
, detail
should be of type QueryList<TemplateRef>
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With