Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular ContentChildren get directive Inputs and TemplateRef

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.

like image 632
Makla Avatar asked Jan 10 '18 18:01

Makla


2 Answers

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

like image 162
yurzui Avatar answered Oct 31 '22 18:10

yurzui


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>.

like image 26
Günter Zöchbauer Avatar answered Oct 31 '22 18:10

Günter Zöchbauer