Making a reusable customised grid column

14 posts, 0 answers
  1. Mojtaba
    Mojtaba  avatar
    36 posts
    Member since:
    Oct 2018

    Posted 14 Nov 2018 Link to this post

    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

  2. T. Tsonev
    Admin
    T. Tsonev avatar
    2833 posts

    Posted 16 Nov 2018 Link to this post

    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.
  3. Mojtaba
    Mojtaba  avatar
    36 posts
    Member since:
    Oct 2018

    Posted 16 Nov 2018 in reply to T. Tsonev Link to this post

    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

  4. T. Tsonev
    Admin
    T. Tsonev avatar
    2833 posts

    Posted 20 Nov 2018 Link to this post

    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.
  5. Mojtaba
    Mojtaba  avatar
    36 posts
    Member since:
    Oct 2018

    Posted 22 Nov 2018 in reply to T. Tsonev Link to this post

    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

  6. Dimiter Topalov
    Admin
    Dimiter Topalov avatar
    1279 posts

    Posted 26 Nov 2018 Link to this post

    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.
  7. Mojtaba
    Mojtaba  avatar
    36 posts
    Member since:
    Oct 2018

    Posted 26 Nov 2018 in reply to Dimiter Topalov Link to this post

    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

  8. Dimiter Topalov
    Admin
    Dimiter Topalov avatar
    1279 posts

    Posted 28 Nov 2018 Link to this post

    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.
  9. Juuso
    Juuso avatar
    2 posts
    Member since:
    May 2019

    Posted 21 May 2019 Link to this post

    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.
     



  10. Svetlin
    Admin
    Svetlin avatar
    444 posts

    Posted 23 May 2019 Link to this post

    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.
  11. balazs
    balazs avatar
    14 posts
    Member since:
    Dec 2014

    Posted 02 Dec 2019 Link to this post

    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);
      }
     
    }

  12. Saravana Kumar
    Saravana Kumar avatar
    22 posts
    Member since:
    Jul 2006

    Posted 18 Aug in reply to balazs Link to this post

    Excellent mate. Helped me today !

    Thank you for this :D

  13. Masaru
    Masaru avatar
    1 posts
    Member since:
    Jan 2019

    Posted 23 Oct in reply to balazs Link to this post

    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.

     

  14. Svetlin
    Admin
    Svetlin avatar
    444 posts

    Posted 27 Oct Link to this post

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

Back to Top