This is a migrated thread and some comments may be shown as answers.

Making a reusable customised grid column

13 Answers 2553 Views
General Discussions
This is a migrated thread and some comments may be shown as answers.
Mojtaba
Top achievements
Rank 1
Mojtaba asked on 14 Nov 2018, 02:18 PM

Hi,

I would like to customise a kendo grid column and reuse it across my application (basically avoiding to repeat some codes related to that column). The following is a very basic example. Let's say I want a customised string grid column. So, I create the component below:

01.@Component({
02.    selector: 'kendo-grid-string-column',
03.    template: `<kendo-grid-column [field]="field" [title]="title">
04.                     </kendo-grid-column>`,
05.})
06.export class KendoGridStringColumnComponent {
07.  @Input()
08.  public field: string;
09. 
10.  @Input()
11.  public title: string;
12. 
13.  constructor() {
14. 
15.    }
16.}

 

Now, I use my component in the grid as follows, but neither that (first) column appears nor I get an error in the console!

1.<kendo-grid [data]="data">
2.    <kendo-grid-string-column field="name" title="Name">
3.    </kendo-grid-string-column>
4.    <kendo-grid-column field="lastName" title="Last Name">
5.    </kendo-grid-column>
6.</kendo-grid>

 

Any help is appreciated.

 

Mojtaba

13 Answers, 1 is accepted

Sort by
0
T. Tsonev
Telerik team
answered on 16 Nov 2018, 08:24 AM
Hello Mojtaba,

Wrapping the column in a custom component will not work as the columns are no longer content children of the Grid.

The recommended way to apply a common behavior is to use a directive, see demo:

<kendo-grid-column myGridColumn>
</kendo-grid-column>


The directive can inject the column and apply the necessary settings:

@Directive({
   selector: '[myGridColumn]'
})
export class MyColumnDirective {
   constructor (private column: ColumnComponent) {
     column.field = 'ProductName';
     column.title = 'Product Name';
   }
}


Best Regards,
T. Tsonev
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Mojtaba
Top achievements
Rank 1
answered on 16 Nov 2018, 09:50 AM

Hi T. Tsonev,

Thank you for your reply.

I see that you can add certain behaviours with Directives to an element in Angular but what about template? Please note that my example above was very basic, and in reality, of course, I want to have a much more complex column including custom edit, custom filter menu etc. Let us say I want a custom editing in my column as below:

01.@Component({
02.    selector: 'kendo-grid-string-column',
03.    template: `<kendo-grid-column [field]="field" [title]="title">
04.  <ng-template kendoGridEditTemplate let-dataItem="dataItem">
05.    Hello, I want to have this text while editting:
06.    <input [(ngModel)]="dataItem[field]" kendoGridFocusable [name]="field" class="k-textbox" required />
07.  </ng-template>
08.</kendo-grid-column>`,
09.})
10.export class KendoGridStringColumnComponent {
11. 
12.  @Input()
13.  public field: string;
14. 
15.  @Input()
16.  public title: string;
17. 
18.  constructor() {
19.  }
20.}

 

Is there any way in Angular that we can also have templates in Directives (I am not aware of such)?

Best regards,

Mojtaba

0
T. Tsonev
Telerik team
answered on 20 Nov 2018, 09:38 AM
Hi,

There's currently no official recommendation on how to share a template across multiple components. The reason is that templates are bound to the context in which they are declared. They're not free to move around as we'd like them to be - see this issue. We're monitoring developments in the framework for improvements in this regard.

The recommended approach is to wrap the entire Grid in a custom component for reuse.

Best Regards,
T. Tsonev
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Mojtaba
Top achievements
Rank 1
answered on 22 Nov 2018, 10:44 AM

Hi,

Thank you for your input.

Eventually I managed to do this by inheriting from ColumnComponent. I am posting my workaround in case might be useful for others. Although this solution is working, Visual studio complaining about import of EditTemplateDirective. I have posted that issue in a separate thread here.

The following is my .ts code:

01.@Component({
02.  providers: [
03.    {
04.      provide: ColumnBase,
05.      useExisting: forwardRef(() => KendoGridStringColumnComponent)
06.    },
07.  ],
08.  selector: 'kendo-grid-string-column',
09.  templateUrl: './kendo-grid-string-column.component.html',
10.})
11.export class KendoGridStringColumnComponent extends ColumnComponent implements AfterViewInit  {
12. 
13.  @Input('required') public required: boolean = false;
14. 
15.  @ViewChild('myInput') public myControl: NgModel;
16. 
17.  @ViewChild(EditTemplateDirective) public editTemplate: EditTemplateDirective;
18.  
19.  constructor(private formInstance: NgForm,@SkipSelf() @Host() @Optional() parent?: ColumnBase) {
20.    super(parent);
21.  }
22. 
23.  ngAfterViewInit(): void {
24.    this.formInstance.control.statusChanges.subscribe(result => {
25.      if (this.myControl && this.myControl.invalid && result === 'VALID')
26.        this.formInstance.control.setErrors({ 'invalid': true });
27.    });
28.  }
29. 
30.  onChange (control: NgModel) {
31.    this.formInstance.control.markAsDirty();
32.    if (control.valid)
33.      this.formInstance.control.updateValueAndValidity();
34.    else
35.      this.formInstance.control.setErrors({ 'invalid': true });
36. 
37.  }
38. 
39.}

and here is the template code.

1.<ng-template kendoGridEditTemplate let-dataItem="dataItem">
2.  <input [(ngModel)]="dataItem[field]"
3.         ngModel #myInput="ngModel" (change)="onChange(myInput)" (keyup)="onChange(myInput)"
4.         kendoGridFocusable
5.         [attr.name]="field" [name]="field" class="k-textbox" [required]="required" />
6.</ng-template>

Regards,

Mojatab

0
Dimiter Topalov
Telerik team
answered on 26 Nov 2018, 09:27 AM
Hello Mojtaba,

The EditTemplate directive is designed to be used as an attribute and not referenced as a type or instantiated outside of the context of the <ng-template kendoGridEditTemplate...> tag put within a Grid ColumnComponent instance's template. That said, if you need a reference, the column's edit template is already available as a ContentChild of the base ColumnComponent type (this is an excerpt of the ColumnComponent code):



The original source code of all our packages is available for you to download and inspect as described in the following section of our documentation:

https://www.telerik.com/kendo-angular-ui-develop/components/installation/source-code/

You can provide the editing template within the <custom-column> markup, e.g.:

<my-column [field]="'ProductName'">
            <ng-template kendoGridEditTemplate let-dataItem="dataItem">
              My Template for
                {{dataItem?.ProductName}}<br />
                <input [(ngModel)]="dataItem.ProductName" name="ProductName" />
    </ng-template>
            </my-column>

@Component({
  selector: 'my-column',
  template: ``,
  providers: [
   {
    provide: ColumnBase,
    useExisting: forwardRef(() => MyGridColumn)
  },
 ]
})
export class MyGridColumn extends ColumnComponent {
  @Input() public field: string;
  ngAfterViewInit() {
    console.log(this.editTemplate)
  }
}

Here is a runnable demo demonstrating the described approach:

https://stackblitz.com/edit/angular-crrt7z-d2wuma?file=app/app.component.ts

I hope this helps.

Regards,
Dimiter Topalov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Mojtaba
Top achievements
Rank 1
answered on 26 Nov 2018, 10:26 AM

Hi Dimiter,

I can understand your solution. However, the way that you implemented your solution means that for any page that I want to use my custom "my-column" I should put KendoGridEditTemplate inside "my-column", while the main point of making a custom column is to avoid repeating a same code for every page by taking out KendoGridEditTemplate from each page to the template of my custom column. I achieved this by using ViewChild instead of ContentChild as I explained above. However, Visual Studio is complaining about the referencing though the solution is working.

So, it might be a good idea to expose KendoGridEditTemplate as well.

Regards,

Mojtaba

0
Dimiter Topalov
Telerik team
answered on 28 Nov 2018, 07:20 AM
Hello Mojtaba,

I will add exposing the EditTemplateDirective to the same GitHub issue so we can consider it too. Thank you for your cooperation and detailed explanation of the use case for the discussed adjustments.

Regards,
Dimiter Topalov
Progress Telerik
Get quickly onboarded and successful with your Telerik and/or Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
0
Juuso
Top achievements
Rank 1
answered on 21 May 2019, 01:02 PM
Hi.

We have some inputs (iconClass, look) for practically every button inside our kendo-grid-command-column. We would like to avoid duplicating those to make changing them easier. I've tried following with no luck:

Extending CommandColumnComponent:
import { Component, ContentChild, forwardRef, Host, Optional, SkipSelf } from '@angular/core';
import { ColumnBase, CommandColumnComponent } from '@progress/kendo-angular-grid';
import { EditCommandDirective } from '@progress/kendo-angular-grid/dist/es2015/editing/edit-command.directive';
  
@Component({
  selector: 'kendo-grid-edit-column',
  templateUrl: './kendo-grid-delete-button.component.html',
  styleUrls: ['./kendo-grid-delete-button.component.scss'],
  providers: [
    {
      provide: ColumnBase,
      useExisting: forwardRef(() => KendoGridDeleteButtonComponent)
    }
  ]
})
 export class KendoGridDeleteButtonComponent extends CommandColumnComponent {
  @ContentChild(EditCommandDirective) public editCommand: EditCommandDirective;
  
  public constructor(@SkipSelf() @Host() @Optional() parent?: ColumnBase) {
    super(parent);
  }
  
  public ngAfterViewInit() {
    this.editCommand.iconClass = 'fa fa-pencil';
    this.editCommand.look = 'flat';
  }
}

Extending CellTemplateDirective:
import { ContentChild, Directive } from '@angular/core';
import { CellTemplateDirective } from '@progress/kendo-angular-grid';
import { EditCommandDirective } from '@progress/kendo-angular-grid/dist/es2015/editing/edit-command.directive';
  
@Directive({
  selector: '[kendoGridCommands]'
})
export class KendoGridCommandsDirective extends CellTemplateDirective {
  @ContentChild(EditCommandDirective) public editCommand: EditCommandDirective;
  
  public ngAfterContentInit() {
    this.editCommand.iconClass = 'fa fa-pencil';
    this.editCommand.look = 'flat';
  }
}

I've also tried template variable declaration (hash sign) and using that variable name when getting ContentChild.

I'd highly appreciate if anyone could tell me how to achieve what I want. It would make our codebase much cleaner.
 



0
Svet
Telerik team
answered on 23 May 2019, 12:20 PM
Hi Juuso,

As the initial topic of the thread is different than the topic of the last request, I created a new ticket with number 1410293 on your behalf, where I have provided a suggested solution. Let's keep the communication there.

For future reference, please open separate threads for questions that are not directly related to each other. This will help us to keep a clean history of the forum and provide a better support service in general. Thank you in advance.

Regards,
Svetlin
Progress Telerik
Get quickly onboarded and successful with your Telerik and Kendo UI products with the Virtual Classroom free technical training, available to all active customers. Learn More.
1
balazs
Top achievements
Rank 2
answered on 02 Dec 2019, 06:56 PM

I've created a solution (it's based on one of Dimiter Topalov's ticket). This should be fully customizable, without loosing flexibility over the original kendo-column...

https://stackblitz.com/edit/angular-oohjvx?file=app/app.component.ts

import { Component, ElementRef, forwardRef, Input, ViewChild, TemplateRef } from '@angular/core';
import { ColumnBase, ColumnComponent, CellTemplateDirective } from '@progress/kendo-angular-grid';
import { A8Consts } from '@ups/angular-common/src/lib/a8consts';
 
@Component({
  selector: 'kendo-grid-column-ex-boolean',
  providers: [{
    provide: ColumnBase,
    useExisting: forwardRef(() => KendoGridColumnExBoolean)
  }],
  template: `
    <ng-template kendoGridCellTemplate let-dataItem>
      <input type="checkbox" [checked]="dataItem[field]"/>
    </ng-template>
  `
})
export class KendoGridColumnExBoolean extends ColumnComponent {
 
  constructor(private el: ElementRef) {
    super();
  }
 
  @ViewChild(CellTemplateDirective /*, { static: true } */ ) viewTemplate: CellTemplateDirective;
 
  public get templateRef(): TemplateRef<any> {
    const template = this.template && this.template.templateRef;
    const viewTemplate = this.viewTemplate && this.viewTemplate.templateRef;
    return  template ? this.template.templateRef : (viewTemplate ? this.viewTemplate.templateRef :  undefined);
  }
 
}

0
Saravana Kumar
Top achievements
Rank 1
answered on 18 Aug 2020, 12:22 PM

Excellent mate. Helped me today !

Thank you for this :D

0
Masaru
Top achievements
Rank 1
answered on 23 Oct 2020, 09:12 AM

The solution is almost perfect for me in my Angular 10 project, but I'm VS Code shows me the following error (Strangely ng build doesn't complain about this):

 

'templateRef' is defined as a property in class 'ColumnComponent', but is overridden here in 'KendoGridColumnExBoolean' as an accessor.ts(2611)

The problem is ColumnComponent has a readonly templateRef field, which we want to override with public get templateRef().

Is the solution just to add  // @ts-ignore and just move on?

It would be very helpful if Kendo provided us an official way of achieving this.

 

0
Svet
Telerik team
answered on 27 Oct 2020, 08:19 AM

Hi Masaru,

At this point there isn't a supported configuration of Kendo UI for Angular that would allow to create a reusable Grid column. Indeed, the limitation is imposed by the Angular framework. To add a bit more, the Grid component as well as other Kendo UI for Angular components use the Angular ContentChildren query in order to get its columns. However if the columns aren't defined within the kendo-grid markup then the ContentChildren query won't catch them:

"Does not retrieve elements or directives that are in other components' templates, since a component's template is always a black box to its ancestors."

To sum up, I understand that this may be disappointing but there isn't a supported way of achieving the requirement of reusing a specific column due to the imposed Angular limitations. This is why, we don't recommend using any of the suggested workarounds in this thread. Respectively we are unable to recommend or not the solution of using // @ts-ignore. The suggestions in this case should be used at the developer own discretion as they don't represent an officially supported functionality of Kendo UI for Angular. 

Regards,
Svetlin
Progress Telerik

Virtual Classroom, the free self-paced technical training that gets you up to speed with Telerik and Kendo UI products quickly just got a fresh new look + new and improved content including a brand new Blazor course! Check it out at https://learn.telerik.com/.

Tags
General Discussions
Asked by
Mojtaba
Top achievements
Rank 1
Answers by
T. Tsonev
Telerik team
Mojtaba
Top achievements
Rank 1
Dimiter Topalov
Telerik team
Juuso
Top achievements
Rank 1
Svet
Telerik team
balazs
Top achievements
Rank 2
Saravana Kumar
Top achievements
Rank 1
Masaru
Top achievements
Rank 1
Share this question
or