Odd behaviour of CellTemplateDirective inside *ngFor

1 Answer 719 Views
Grid
Missing User
Missing User asked on 07 Oct 2021, 09:13 AM | edited on 07 Oct 2021, 09:45 AM

Hi everyone, I'm currently working on a grid which is dynamically built and I'm experiencing some weird behaviour.

I'll try to do my best to explain it clearly, starting from the code of the grid:

<kendo-grid
        [kendoGridBinding]="kgData"
        [sortable]="true"
        [pageable]="true"
        [pageSize]="5"
        >
        <!-- BUTTONS -->
        <ng-template kendoGridToolbarTemplate>
          <button kendoGridAddCommand>Aggiungi record</button>
          <button class="k-button">
            Salva
          </button>
        </ng-template>
        <!-- HEADERS -->
        <kendo-grid-column *ngFor="let col of columnInfoArray" [field]="col.fieldname" [title]="col.description" [hidden]="col.fieldname === 'id'">
          <ng-template
            kendoGridCellTemplate
            let-dataItem>
              <!--! NUMBER  -->
              <!--
                decimals: do not allow the user to type , and . when focused
                format n0: do not put commas when the input is not focused
              -->
              <kendo-numerictextbox
                *ngIf="col.configtype === 'int'"
                kendoGridFocusable
                [decimals]="0"
                format="n0"
                [value]="dataItem[col.fieldname]"
                (valueChange)="kgUpdateValue(col.fieldname,dataItem,$event)"
                ></kendo-numerictextbox>
              <!--! SELECT  -->
              <!--
                [data]: the array of options, textField and valueField access the properties of the objects contained inside the array
                [value]: gets the default value based on the fieldname and the value
              -->
              <kendo-dropdownlist
                *ngIf="col.configtype === 'combo'"
                kendoGridFocusable
                [data]='kgSelectOptions[col.fieldname]'
                textField='label'
                valueField='value'
                [value]='kgGetDefaultOption(col.fieldname,dataItem[col.fieldname])'
                (valueChange)="kgUpdateValue(col.fieldname,dataItem,$event.value)"
                ></kendo-dropdownlist>
              <!--! CHECKBOX  -->
              <input
                *ngIf="col.configtype === 'bool'"
                kendoGridFocusable
                type="checkbox"
                kendoCheckBox
                (change)="kgUpdateValueEvent(col.fieldname,dataItem,$event)"
                />
              <!--! STRING  -->
              <input
                *ngIf="col.configtype === 'string'"
                kendoGridFocusable
                (change)="kgUpdateValueEvent(col.fieldname,dataItem,$event)"/>
          </ng-template>
        </kendo-grid-column>
      </kendo-grid>


columnInfoArray is an array of objects like this: (of course with different values every time) 

{
idfield: 1,
description: "Widget",
fieldname: "idwidget",
mandatory: true,
actionget: "widget",
configtype: "combo",
deleted: false,
filter: false
}

This tells the code the "type" of input I'm expecting, like a select box, a number, a textarea, a checkbox etc etc.
NOTE: despite I'm talking about inputs, the table isn't inside any form and forms aren't needed to get the job done.
The actionget, when present, is just a string that will be concatenated to a link and it'll get an array of objects that will look like:

{
   label: 'Some text',
   value: 1
}

There are two things that I don't understand
  1. inside the kendo-dropdownlist you can see the function kgGetDefaultOption. Inside this function I have put a console.log of a counter that starts from 0 and gets incresed every time the function is called: my kgData consists of 7 records, and I have two columns with configtype == 'combo'. I was expecting for the counter to be 14, but I get 89 instead. Also, the function is called every time I change page on the pagination bar. Why does that happen?
  2. if I check the checkbox on row 1 of page 1, every checkbox on row1 of other pages is checked as well (I'm providing a GIF that shows what I mean)

 

Unfortunately I can't provide a StackBlitz (the API calls that get the data don't work on it). I hope this is enough for you to help me, else I will provide more info.

PS: even if that esulates from the main question, if you think something can be improved for what I need, let me know! This is the very first time I'm using Kendo grid and to be honest I found the documentation really difficult to understand so I might've missed something.



1 Answer, 1 is accepted

Sort by
1
Accepted
Martin Bechev
Telerik team
answered on 12 Oct 2021, 08:41 AM

Hi Marco,

Thank you for the provided details and feedback.

I will answer in the same order as the questions have been asked.

1. Indeed I am not sure why the custom function is called so many times based on the current information. I tried to reproduce it in the following example, but the custom function is invoked for each row of the current page:

https://stackblitz.com/edit/angular-zzrssj

I also tested the above markup in an isolated Angular 12 project, but it behaves the same.

As for the paging, it is expected the function to be invoked when the page is changed, as the Grid rerenders the data in order to show the corresponding records of the next/previous page.

In general, if a template is used for grid calculations (for example if a Cell Template is used to calculate each cell's value), then this could reflect the Grid performance since the function will be invoked for each cell. That is why we want to suggest binding the value of the DropDownList directly to the dataItem (that comes with the template) if possible, to avoid calling the function for each cell, e.g.:

        <ng-template kendoGridCellTemplate let-dataItem="dataItem" >
        <kendo-dropdownlist
               ...
                [data]="products"
                [value]="dataItem.Product"
                ></kendo-dropdownlist>
         </ng-template>

2. Regarding this point, I noticed that the checkbox value option is not bound. What can be done is to set the checked property to a field of the dataItem and on change event to update the respective field. Thus the checked state will be persistent.

 <input
       ...
        [checked]="dataItem.Discontinued"
        (change)="checkboxChange(column.fieldname,dataItem,$event)"
        />
  checkboxChange(field, dataItem, e) {
    const index = this.gridData.data.indexOf(dataItem);
    this.gridData.data[index].Discontinued =
      !this.gridData.data[index].Discontinued;
  }

Please refer to the following example:

https://stackblitz.com/edit/angular-qokwcu

Let me know how it goes.

Regards,
Martin
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/.

Missing User
commented on 14 Oct 2021, 11:47 AM

Hello, thanks for your explanation, I was able to make the checkbox work!
Tags
Grid
Asked by
Missing User
Answers by
Martin Bechev
Telerik team
Share this question
or