Hi,
We are several developers in our group and using k-grid in our forms. We need to follow some standards for our grids both in appearance and behavior. I am trying make a component based on the grid with predefined options. The grid works fine but I can't add the column using ng-content.
My component <a-grid>
<
kendo-grid
[data]="data" [resizable]="resizable" [pageable]="pageable" [pageSize]="state.take" [skip]="state.skip" [sortable]="{allowUnsort: true}" [sort]="sort" (sortChange)="sortChange($event)" (pageChange)="pageChange($event)" [skip]="0" [filter]="state.filter"
filterable
=
"menu"
(dataStateChange)="dataStateChange($event)"
class
=
"grid"
>
<
ng-content
></
ng-content
>
</
kendo-grid
>
Usage:
<
a-grid
[data]="gridView" [excelFilename]="getFilename()">
<
kendo-grid-column
a-col
field
=
"Anst_ID"
title
=
"Anst ID"
*
ngIf
=
"showID"
>
<
ng-template
kendoGridFilterMenuTemplate let-filter
let-column
=
"column"
let-filterService
=
"filterService"
>
<
kendo-grid-string-filter-menu
[column]="column" [filter]="filter" [filterService]="filterService" [extra]="false"
operator
=
"contains"
>
</
kendo-grid-string-filter-menu
>
</
ng-template
>
</
kendo-grid-column
>
<
kendo-grid-column
field
=
"Anstnr"
title
=
"Anstnr"
>
<
ng-template
kendoGridFilterMenuTemplate let-filter
let-column
=
"column"
let-filterService
=
"filterService"
>
<
kendo-grid-string-filter-menu
[column]="column" [filter]="filter" [filterService]="filterService" [extra]="false"
operator
=
"contains"
>
</
kendo-grid-string-filter-menu
>
</
ng-template
>
</
kendo-grid-column
>
<
kendo-grid-column
field
=
"EfterFornamn"
title
=
"Namn"
>
<
ng-template
kendoGridFilterMenuTemplate let-filter
let-column
=
"column"
let-filterService
=
"filterService"
>
<
kendo-grid-string-filter-menu
[column]="column" [filter]="filter" [filterService]="filterService" [extra]="false"
operator
=
"contains"
>
</
kendo-grid-string-filter-menu
>
</
ng-template
>
<
ng-template
kendoGridCellTemplate let-dataItem>
{{dataItem.EfterFornamn}}
</
ng-template
>
</
kendo-grid-column
>
</
a-grid
>
On the next step I am going to do the same thing for column component. How is it possible?
Regards,
/Pouya
11 Answers, 1 is accepted
The described approach will indeed not work due to Angular-specific timing-related reasons. This is why such a functionality is not supported out-of-the-box, and currently the only way to achieve it is by obtaining a reference to all columns via @ContentChildren in the wrapper component, obtain a reference to the Grid as well, and use the gridReference.columns.reset() method, passing it an array, created from the previously obtained via @ContentChildren collection of columns.
Here is a sample implementation of the described approach:
http://plnkr.co/edit/RkTp2FLKGJgYopEStIhB?p=preview
I hope this helps.
Dimiter Topalov
Progress Telerik
Hello Dimiter
I stumbled upon a similar problem when trying to wrap the `kendo-treelist` component and then nesting `kendo-treelist-column` components in it.
However, it does not matter if I use `ng-content` or your solution using `@ContentChildren`, it will fail because of the following error:
ERROR Error: Uncaught (in promise): NullInjectorError: R3InjectorError(ProductTemplateManagementModule)[OptionChangesService -> OptionChangesService -> OptionChangesService -> OptionChangesService]:
NullInjectorError: No provider for OptionChangesService!
Error: NullInjectorError: No provider for OptionChangesService!
How can this be solved? Maybe the proposed solution is outdated anyways as it is from 2018?
Thanks in advance!
I wonder that could you fix this problem ? I am making standard treelist based on kendo treelist angular but i also have this problem when i use kendo-treelist-column in <app-tree></app-tree>. If you could deal with this problem, please give me some advices.
Thanks in advance.

Hi Dimitri,
Thanks to your code, I implemented my component as below. But I had to have a container component as it is done here:
http://plnkr.co/edit/oM9X1TX8OXz1eDcUDl6l?p=preview
a-column:
<
kendo-grid-column
#column
field
=
"field"
title
=
"title"
>
<
ng-template
kendoGridFilterMenuTemplate let-filter
let-column
=
"column"
let-filterService
=
"filterService"
>
<
kendo-grid-string-filter-menu
[column]="column" [filter]="filter" [filterService]="filterService" [extra]="false"
operator
=
"contains"
>
</
kendo-grid-string-filter-menu
>
</
ng-template
>
</
kendo-grid-column
>
a-column-container:
@Directive({
selector:
'[ColumnContainer]'
})
export class AColumnContainerDirective {
@ContentChildren(AColumnComponent, { descendants:
true
}) private columns: QueryList<AColumnComponent>
allColumns(): Array<ColumnComponent> {
return
!!
this
.columns ?
this
.columns.toArray() :
null
}
}
@Component({
selector:
'a-column-container'
,
template: '
<div ColumnContainer>
<ng-content></ng-content>
</div>
',
})
export class AColumnContainerComponent {
@ContentChild(AColumnContainerDirective) container: AColumnContainerDirective;
allColumns() {
return
this
.container.allColumns()
}
}
a-grid:
<
kendo-grid
#grid [data]="data" [resizable]="resizable" [pageable]="pageable" [pageSize]="state.take" [skip]="state.skip"
[sortable]="{allowUnsort: true}" [sort]="sort" (sortChange)="sortChange($event)" (pageChange)="pageChange($event)" [skip]="0"
[filter]="state.filter"
filterable
=
"menu"
(dataStateChange)="dataStateChange($event)"
class
=
"grid"
>
<
ng-content
></
ng-content
>
<
kendo-grid-messages
noRecords
=
"Inga data"
filterFilterButton
=
"Filtrera"
filterClearButton
=
"Rensa"
pagerItems
=
""
pagerItemsPerPage
=
"poster per sida"
pagerFirstPage
=
"Första sidan"
pagerLastPage
=
"Sista sidan"
pagerNextPage
=
"Nästa sida"
pagerOf
=
"av"
pagerPage
=
"Sida"
pagerPreviousPage
=
"Föregående sida"
>
</
kendo-grid-messages
>
<!-- Export to Excel -->
<
kendo-grid-excel
[fileName]="excelFilename">
</
kendo-grid-excel
>
<
ng-template
kendoPagerTemplate
let-totalPages
=
"totalPages"
let-currentPage
=
"currentPage"
>
<
kendo-pager-prev-buttons
></
kendo-pager-prev-buttons
>
<
kendo-pager-numeric-buttons
[buttonCount]="10"></
kendo-pager-numeric-buttons
>
<
kendo-pager-next-buttons
></
kendo-pager-next-buttons
>
<
kendo-pager-info
></
kendo-pager-info
>
<
kendo-pager-page-sizes
[pageSizes]="[50, 100, 200]"></
kendo-pager-page-sizes
>
</
ng-template
>
</
kendo-grid
>
and in my form:
<
a-grid
[data]="gridView" [excelFilename]="getFilename()" [columnContainer]="columnContainer" >
<
a-column-container
ColumnContainer>
<
a-column
field
=
"Anstnr"
title
=
"Anstnr"
></
a-column
>
<
a-column
field
=
"EfterFornamn"
title
=
"Namn"
></
a-column
>
</
a-column-container
>
</
a-grid
>
The ugly part is I have to add this line in .ts file and actually send the column container to the grid component:
@ViewChild(AColumnContainerDirective) columnContainer: AColumnContainerDirective;
I tried hard to do this in one of a-components but I got no luck. Is there any way to do so?
Thanks in advance
/Pouya
Aditro Enterprise
I am not sure I fully understand the expressed concern, as some pieces of the puzzle are missing (for example the component code for the custom column and Grid wrappers), but I think I understand the idea.
If this line of code:
@ViewChild(AColumnContainerDirective) columnContainer: AColumnContainerDirective;
... is used in the "a-grid" component code in order to obtain a reference to the custom columns container (a-column-container) so that you can call the allColumns() method to obtain the columns that need to be further passed to the Grid component's columns.reset() method, this is necessary for all content projection to work as expected, and generally should not bother you.
Can you please describe the last part of the question in further details?
"The ugly part is I have to add this line in .ts file and actually send the column container to the grid component:
...
I tried hard to do this in one of a-components but I got no luck. Is there any way to do so?"
What in the described approach you have doubts about, and what a-components exactly are you referring to? What exactly is the desired workflow for passing the actual Grid columns (kendo-column-component) to the custom Grid wrapper (a-grid)? If we gain a better understanding of the specific scenario, we might try providing a solution that is best suitable to the specific scenario, but in general the one discussed so far seems the best (and possibly - the only) option.
Regards,
Dimiter Topalov
Progress Telerik

Hi,
Sorry for late reply. I faced a bigger limitation that might make us not to follow this approach. Apparently columns.reset() does not copy the template and we need to be able to customize the filter of each column.
I was going to show the problem in a Plunker but I got an error by clicking the filter button:
The error
StaticInjectorError[e]:
NullInjectorError: No provider for e!
My plunker:
http://plnkr.co/edit/rm3kh6BjwgBGa52L?preview
Can you take a look at this first, so I will be able to continue making the situation on Plunker? And also if you know how to copy the column including all its properties/templates, please provide me some information.
Best regards,
/Pouya
Aditro Enterprise
The described error is caused by the fact that the built-in Grid FilterMenu templates internally use the LocalizationService that is provided in the context of the Grid. When the column definition markup is outside of the Grid component, this service cannot be found.
There are two options to use the Grid Filter Menu in the described scenario:
1) Do not use templates and rely on the built-in Grid Filter Menu layout and functionality
2) Use a reusable custom Filter Menu component as described in the following section of our documentation:
https://www.telerik.com/kendo-angular-ui/components/grid/filtering/reusable-filter/#toc-filter-menu
Here is the updated Plunker that demonstrates both approaches (the custom filter menu is not functional, but shows that the container is opened and rendered with no errors):
http://plnkr.co/edit/Jf5U11RI170jGtkiNhMd?preview&p=preview
I hope this helps.
Regards,
Dimiter Topalov
Progress Telerik
Hi Dimiter, I'm experiencing the following issue and can't find a solution:
I have a parent base-grid component where I transclude all columns like this:
<kendo-grid>
<!-- Content projection for section columns-->
<ng-content></ng-content>
</kendo-grid>
The columns are added dynamically and their definition markup is in a different file, like this:
<app-base-grid>
<kendo-grid-column >
<ng-template kendoGridFilterMenuTemplate let-filter let-column="column" let-filterService="filterService">
<kendo-grid-string-filter-menu [column]="column" [filter]="filter" [filterService]="filterService" operator="eq">
</kendo-grid-string-filter-menu>
</ng-template>
<ng-template kendoGridCellTemplate let-dataItem let-rowIndex>
...
</ng-template>
</kendo-grid-column>
</app-base-grid>
I'm using @ContentChildren(ColumnComponent) columns: QueryList<ColumnComponent>; to get the transcluded columns and @ViewChild(GridComponent) grid: GridComponent; for the grid component.
I'm also using this.grid.columns.reset(cols); to update grid cols.
Everything works fine but I'm struggling to make the built-in grid filters work.
The problem is that when I want to use a built-in Grid Filter Menu in one of the transcluded columns I'm getting the following error:
"StaticInjectorError(AppModule) [LocalizationService -> InjectionToken Localization key prefix]: StaticInjectorError(Platform: core) [LocalizationService -> InjectionToken Localization key prefix]: NullInjectorError: No provider for InjectionToken Localization key prefix".
The only way I found to avoid this error is adding " { provide: L10N_PREFIX, useValue: ' ' } "to the component's providers list but since I'm not passing an empty string as a value to use, all the filter menu options are blank.
Do you know which value should I use for the L10N_PREFIX token or if there's any other way to pass the parent kendo grid context to the transcluded columns or if there's any other workaround?
Thanks in advance!

Hi,
Your code works. I applied your code exactly on my project but There's problem. The columns don't load until I click once somewhere on the page!
Let's assume I have this data:
gridData = [{
ColA:
"value1"
}, {
ColA:
"value2"
}]
and my grid is like:
<a-grid [data]=
"gridData"
>
<a-column field=
"ColA"
title=
"Column A"
>
<ng-template kendoGridFilterMenuTemplate let-column=
"column"
let-filter=
"filter"
let-filterService=
"filterService"
>
<string-menu-filter [column]=
"column"
[filter]=
"filter"
[filterService]=
"filterService"
[extra]=
"false"
operator=
"contains"
>
</string-menu-filter>
</ng-template>
</a-column>
</a-grid>
When the page loads, it shows "ColA" as title. When I click on the page, It turns to "Column A" and the filter to the custom filter.
I couldn't simulate this behavior on Plunker but I recorded it as a GIF which I attach.
Regards,
/Pouya
The described undesired behavior and provided GIF suggest that the Grid is initially rendered using its automatic column generation based on the data it is bound to, and after the Angular Change Detection mechanism is triggered (by clicking on the page), the custom columns are rendered.
This could be due to a timing issue (the custom columns are created after the Grid is rendered with the auto-generated columns) or the components might use ChangeDetectionStrategy.OnPush. If the latter is the case, you can trigger the Angular change detection manually via calling the ChangeDetectorRef.detectChanges() method.
The fact that the undesired behavior cannot be simulated in a Plunker suggests that it is caused by some specifics of the custom implementation.
Typically assistance with custom-tailored solutions and custom implementation of features that are not supported out-of-the-box falls in the scope of our separate dedicated offering - the Progress Professional Services that specialize in these areas:
https://www.progress.com/services/outsourcing
Let me know if you are interested in benefiting from their expertise, and I will arrange for someone from the team to contact you.
However, if you send us an isolated runnable project where the problem can be observed (does not have to be necessarily a Plunker), we will also do our best to inspect it further and provide a suggestion that is most suitable to the specific scenario. Thank you in advance.
Regards,
Dimiter Topalov
Progress Telerik

Hi Dimiter,
It works like a charm! I just added ChangeDetectorRef.detectChanges() after reseting columns in the Grid component and I don't need to wrap the columns in a container anymore. Everything works ok (so far)
Thank you for your time and help.
Best regards,
/Pouya
Aditro Enterprise AB

Hello,
I have a same need of making the components on the kendo grid and on the columns.
I'm trying to apply the same approach as described in the given post.
Here my code: http://plnkr.co/edit/0yIsZt7llTWZnO13ScEi?p=preview
My issue is that the columns <di-custom-grid-column> are not shown. However if using <kendo-grid-column>, the columns appear well.
<
di-custom-grid
[data]="gridData" [height]="200">
<
di-custom-grid-column
field
=
"ProductID"
title
=
"ID"
width
=
"40"
>
</
di-custom-grid-column
>
<
kendo-grid-column
field
=
"ProductName"
title
=
"Name"
width
=
"250"
>
</
kendo-grid-column
>
</
di-custom-grid
>
Could you help me ?
Thank you in advance.
Pavlo
The provided example was not runnable, but I managed to extract the relevant code into a runnable Stackblitz demo. The columns within the custom Grid are not visible because they are not real Grid ColumnComponent instances (what are required to be passed to the Grid columns.reset() method and rendered in the Grid similar to the previously discussed scenarios).
You can obtain a reference to the underlying real ColumnComponent and pass these to the columns.reset() method instead, e.g.:
https://stackblitz.com/edit/angular-rfwga6-m9pgkg?file=app/di-custom-grid.component.ts
I hope this helps, but please note that the scenario and any custom implementations in general are not supported out-of-the-box, and we typically recommend the Progress Professional Services that specialize in custom-tailored solutions, general consulting, custom implementation, feature customization, and more (also suggested in the response from 10-May-2018 that is marked as answer):
https://www.progress.com/services/outsourcing
Regards,
Dimiter Topalov
Progress Telerik

Hi Dimiter,
I'm experiencing the following issue and can't find a solution:
I have a parent base-grid component where I transclude all columns like this:
<kendo-grid>
<!-- Content projection for section columns-->
<ng-content></ng-content>
</kendo-grid>
The columns are added dynamically and their definition markup is in a different file, like this:
<app-base-grid>
<kendo-grid-column >
<ng-template kendoGridFilterMenuTemplate let-filter let-column="column" let-filterService="filterService">
<kendo-grid-string-filter-menu [column]="column" [filter]="filter" [filterService]="filterService" operator="eq">
</kendo-grid-string-filter-menu>
</ng-template>
<ng-template kendoGridCellTemplate let-dataItem let-rowIndex>
...
</ng-template>
</kendo-grid-column>
</app-base-grid>
I'm using @ContentChildren(ColumnComponent) columns: QueryList<ColumnComponent>; to get the transcluded columns and @ViewChild(GridComponent) grid: GridComponent; for the grid component.
I'm also using this.grid.columns.reset(cols); to update grid cols.
Everything works fine but I'm struggling to make the built-in grid filters work.
The problem is that when I want to use a built-in Grid Filter Menu in one of the transcluded columns I'm getting the following error:
"StaticInjectorError(AppModule) [LocalizationService -> InjectionToken Localization key prefix]: StaticInjectorError(Platform: core) [LocalizationService -> InjectionToken Localization key prefix]: NullInjectorError: No provider for InjectionToken Localization key prefix".
The only way I found to avoid this error is adding " { provide: L10N_PREFIX, useValue: ' ' } "to the component's providers list but since I'm passing an empty string as a value to use, all the filter menu options are blank.
Do you know which value should I use for the L10N_PREFIX token or if there's any other way to pass the parent kendo grid context to the transcluded columns or if there's any other workaround?
Thanks in advance!