Reputation: 204
I have a component called select.component (selector: 'app-select') which searches for users using typeahead (I left this out for simplicity). I then want to show the name and employeeId by binding to item in ng-template.
<ng-select>
<ng-template ng-option-tmp let-item="item">
{{item.name}}</br>
{{item.employeeId}}
</ng-template
</ng-select>
As we use many of these ng-select elements in the app, I have made select.component a reusable component which other parent components use. I therefore want to make the ng-template part dynamic and have it passed in from the parent component. I have changed select.component to look like this instead:
<ng-select>
<ng-template ng-option-tmp let-item="item">
<ng-content></ng-content>
</ng-template
</ng-select>
I then have user.component which uses content projection to populate ng-template in the select.component like so:
<app-select>
{{item.name}}</br>
{{item.employeeId}}
</app-select>
The problem here is that by the time content projection has passed the inner html of app-select to ng-template, {{item.name}} and {{item.employeeId}} are not evaluated within the ng-template. In the dropdown of items, I see {{item.name}}, rather than the actual name value.
How could I best achieve this? I really need parent components to be able to specify how the dropdown items should appear to make select.component fully reusable.
See my stackblitz
Upvotes: 4
Views: 4159
Reputation: 143
I agree it is helpful to encapsulate common setup in a component and allow for the rest of the setup to be provided by the consumer of the component.
NgSelect uses @ContentChild()
to get templates provided within the component tag when the parent is providing them. I suspect <ng-content>
is not yet available because the ngAfterContentInit
lifecycle hook is called before ngAfterViewInit
.
Another approach would be to accept a TemplateRef
as an input to your <app-select>
and set NgSelect's optionTemplate
manually, after the view has initialized:
export class SelectComponent implements AfterViewInit {
@Input() items: any[];
@Input() optionTemplate: TemplateRef<any>; // The options template to provide to ng-select
@ViewChild(NgSelectComponent) ngSelect: NgSelectComponent; // A reference to the ng-select component
// Once @ViewChild is defined after view init
ngAfterViewInit() {
if (this.optionTemplate) {
// Manually assign ng-select's optionTemplate from the input
this.ngSelect.optionTemplate = this.optionTemplate;
}
}
}
Then you can pass in a template you can reference via a reference variable:
<app-select [items]="items" [optionTemplate]="appSelectOption">
</app-select>
<ng-template #appSelectOption let-item="item">
{{item.name}}<br/>
{{item.employeeId}}
</ng-template>
This way you can keep the template outlet context (let-item="item"
) in the same area where you are using the variable.
See a fork of your StackBlitz
Upvotes: 1
Reputation: 27293
You need to pass the code in component and catch in select component
<app-select>
<ng-select [items]="items" bindLabel="name" placeholder="Works">
<ng-template ng-option-tmp let-item="item">
{{item.name}}<br/>
{{item.employeeId}}
</ng-template>
</ng-select>
</app-select>
Then you need to use
<ng-content></ng-content>
in your select component
This way you will be able to achive what you want.
Upvotes: 1
Reputation: 2582
ng-template is never ever work without any stucture directive or template ref variable. you can use ng-container instead of ng-template.sorry any thing is missing coz i have answered this question from my cell phone
Upvotes: 0