Is it possible to write reusable ng-template? A lot of my components use exactly the same ng-template.
For example:
<kendo-grid>
<kendo-grid-column field="group">
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-formGroup="form">
<kendo-dropdownlist #listGroups [data]="groups"
textField="title"
valueField="id"
[valuePrimitive]="true"
[filterable]="true"
[formControl]="form.get('groupId')">
</kendo-dropdownlist>
</ng-template>
</kendo-grid-column>
</kendo-grid>
I do not want to repeat this template and logic behind in all my components. I could write custom component and shrink this code to:
<kendo-grid>
<kendo-grid-column field="group">
<ng-template kendoGridEditTemplate let-dataItem="dataItem" let-formGroup="form">
<my-custom-component [formControl]="form.get('groupId')">
</my-custom-component>
</ng-template>
</kendo-grid-column>
</kendo-grid>
but I want to do more:
<kendo-grid>
<kendo-grid-column field="group">
<ng-template [ngTemplateOutlet]="templateFromAnotherSource">
</ng-template>
</kendo-grid-column>
</kendo-grid>
I found this thread and this which describes ngTemplateOutlet, but not how to share templates between multiple components.
2 Answers 2
The problem is that ng-template has to be compiled. To understand why read Here is what you need to know about dynamic components in Angular.
Now, a template can only be compiled as part of a component right now. So you need to define a component with this template and then you can get access to this template factory either using viewChild or a directive. With viewChild you depend on life-cycle hooks and change detection so I'd go with directive approach. Here is the pseudocode:
@Component() {
template: '<ng-template requestor>...</ng-template>'
}
class NgTemplateProviderComponent {
t: TemplateRef;
register(t) {
this.t = t;
}
}
@Directive() {
selector: 'requestor;
}
class TemplateRequestor {
constructor(t: TemplateRef, p: NgTemplateProviderComponent) {
p.register(t);
}
}
To learn more about this approach of using a directive to get templateRef read Here is how to get ViewContainerRef before @ViewChild query is evaluated.
Then, you need to get access to the NgTemplateProviderComponent component factory, create its instance to get the templateRef:
class SomeComponent {
constructor(r: ComponentFactoryResolver, i: Injector) {
const f = r.resolveComponentFactory(NgTemplateProviderComponent);
const templateRef =f.create(i).instance.t;
}
}
and only then you can use ngTemplateOutlet directive to render the template.
2 Comments
A different approach to solve this problem is to use ng-container to display your content. As Max Koretskyi posted, you can follow a class directive approach in order to make your component reusable in any module. However, if you want just a simple way to make your HTML looks a little bit ugly when same code is needed (also DRY principle) you can do this:
<ng-template #YOUR-NG-TEMPLATE-NAME let-YOUR-PARAMETER="YOUR-PARAMETER">
// Your large implementation
</ng-template>
...
<kendo-grid-column field="whatever">
<ng-template kendoGridEditTemplate let-YOUR-PARAMETER="YOUR-VARIABLE">
<ng-container [ngTemplateOutlet]="YOUR-NG-TEMPLATE-NAME"
[ngTemplateOutletContext]="{ YOUR-PARAMETER: YOUR-PARAMETER }">
</ng-container>
</ng-template>
</kendo-grid-column>
This can be useful when a large amount of code is needed to be reusable, if you just want to display attributes, just use ng-template with some Kendo directive as usual.
Hopefully this helps you.
Comments
Explore related questions
See similar questions with these tags.