Grid 'Batch' editing does not work on column with dropdownlist with object binding

Hi, 

 I am struggling to make batch edit mode work on column edited via a dropdown list bound to an object (Country with attributes {id, label, code}). The problem does not occur in Inline editing. I attached my code for reproduction.

When the previous column's value is empty/undefined and then I select a value from the dropdown list, the cell is correctly highlighted as dirty (via light green background color):

Initial Load with Country emptyImage_3062_1743530717943

Country changed Image_6490_1743530802014

Image_7189_1743530839179

I save the changes via Update buttonImage_5277_1743530920680
Now, I edit again the cell and select another country. The cell is not highlighted as "dirty".Image_2355_1743530967905
Image_8584_1743531011905

 As you can see in the last step above, the cell was not highlighted as "dirty" and the Update button was not enabled.

This problem does not occur when the cell is bound to a primitive value. 

Do you know how to overcome this problem, please ?  Maybe someone can guide me to use a hook to override the cell "equal" logic between "peviousValue" and "value to support object binding ?

I tried override the behavior with IEditCell interface without success.

Please find my code below (Syncfusion version 29.1.x): 

import { FormsModule } from '@angular/forms'
import { GridModule, EditService, ToolbarService, GridComponent } from '@syncfusion/ej2-angular-grids'
import { DropDownListComponent, DropDownListModule } from '@syncfusion/ej2-angular-dropdowns'
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs'
import { AutoCompleteModule } from '@syncfusion/ej2-angular-dropdowns'
import { Component, OnInit, ViewChild } from '@angular/core';

@Component({
  selector: 'nav-tab-grid',
  template: `
    <style>
      .control-section{margin-top: 50px;}        
    </style>
    <div class="control-section">
      <div class="col-lg-9">
        <ejs-grid #normalgrid id='Normalgrid' [dataSource]='gridData'      
        [editSettings]='{ allowEditing: true, allowAdding: true, allowDeleting: true, mode: "Batch" }'
        [toolbar]="['Add', 'Edit', 'Delete', 'Update', 'Cancel']"
        (cellSave)="cellSave($event)"
        >
        <e-columns>
              <e-column uid="id" field='id' headerText='ID' width='140' textAlign='Right' isPrimaryKey="true"></e-column>
              <e-column uid="name" field='name' headerText='Name' width='140' textAlign='Right'></e-column>
              <e-column uid="country" field='country' headerText='Country' width='140' textAlign='Right'>
                <ng-template #template let-data>
                  @if (data) {                                  
                    {{ data?.country?.label }}
                  }
                </ng-template>
                <ng-template #editTemplate let-data>
                  <ejs-dropdownlist #dropdown
                    [dataSource]="[
                      {label: 'Japan', code: 'JPN', id:1},
                      {label: 'Spain', code: 'SPN', id:2},
                      {label: 'Chile', code: 'CHE', id:3}]"
                    allowObjectBinding="true"                    
                    [value]="data?.country?.label === undefined ? {label: ''} : data.country"
                    [fields]="{text: 'label'}">
                  </ejs-dropdownlist>
                </ng-template>
              </e-column>              
        </e-columns>
        </ejs-grid>
      </div>    
    </div>
  `,
  providers: [EditService, ToolbarService],
  standalone: true,
  imports: [GridModule, FormsModule, FormsModule,
            TextBoxModule, AutoCompleteModule, DropDownListModule],
})
export class NavTabGrid implements OnInit {

  @ViewChild(GridComponent) dataGrid!: GridComponent;
 
  @ViewChild(DropDownListComponent) drowpdown!: DropDownListComponent;

  gridData: Object[] = []  
 
  constructor() {}

  public ngOnInit(): void {            
    this.gridData = [
      {id: 1, name: "One", country: null},
      {id: 2, name: "Two", country: {label: 'Spain', code: 'SPN', id:2}},
      {id: 3, name: "Three", country: {label: 'Chile', code: 'CHE', id:3}},
      {id: 4, name: "Four", country: {label: 'Spain', code: 'SPN', id:2}},
    ];
  }

  cellSave(args: any) {
    if(this.drowpdown) {
      args.value = this.drowpdown.value;      
    }
  }
 }


7 Replies

KM Kishore Muthukrishnan Syncfusion Team April 2, 2025 01:43 PM UTC

Hi ,


Greetings from Syncfusion support,


We have noticed that you have used object data for country column and rendered its label value by using column template. To access the value in the object, you can use the complex data binding to directly access nested values. By specifying the field property with a dot (.) notation (e.g., country.label), the grid will automatically bind to the correct property. Additionally, in the cellSave event, store the label value instead of entire object. This approach simplifies data handling and ensures proper binding.


Please refer to the below code snippet and sample for more information.


<e-column uid="country" field='country.label' headerText='Country' width='140' textAlign='Right' >
 

cellSave(args: any) {

    if(this.drowpdown) {

      args.value = (this.drowpdown.value as any).label;     

    }

  }


Documentation : https://ej2.syncfusion.com/react/documentation/grid/columns/column-rendering#complex-data-binding


Sample : https://stackblitz.com/edit/angular-kmddwkc3?file=src%2Fapp.component.ts
 

If you need further assistance, feel free to let us know!


Regards,

Kishore Muthukrishnan



ID Issam DANO April 7, 2025 09:53 AM UTC

Hi  Kishore,

 Thank you very much for your answer. 

 Indeed when bind the column's field to "country.label" and update "cellSave" to what you suggest, I get the cell switch to dirty style as expected. However, this raised another problem: the "country" column does not contain any more the object "country = {id="x", label="y", code="z"}" but only the label because of "cellSave" logic. 

 Do you have another way to keep the object bound the column "country" ? 


Thanks in adance,

Sam



KM Kishore Muthukrishnan Syncfusion Team April 15, 2025 08:09 AM UTC

Hi ,

 

The Syncfusion EJ2 Grid supports the following column types: string, number, boolean, date, datetime, and checkbox. These types allow for appropriate formatting, filtering, and editing based on the underlying data type.

 

If you are working with nested data, the grid also supports complex binding. This means you can bind to a specific field within a nested object (e.g., employee.name). However, please note that only the specified field is retrieved and rendered in the grid, not the entire nested object. This is by design and is the default behavior of the grid to ensure optimal performance. Please refer to the below documentation for more information about column types.

 

Documentation: https://helpej2.syncfusion.com/angular/documentation/grid/columns/columns#column-types
 

Regards,

Kishore Muthukrishnan



ID Issam DANO May 14, 2025 03:29 PM UTC

Hi Kishore,

 Thank you very much for your answer. 

 I continued to bind complex object as you advised but I am facing a new issue related to complex object binding in Excel type filtering ([filterSettings]='{ type: "Excel"}') of ejs-grid. When the nested object in the row data ​is not null, the filtering is working as expected as you can see below:

Image_2708_1747235817724

Filtering on column residence country:

Image_4328_1747235880230

When the row contains a null nested object (for instance, birth country column has a null value), the filter popup throws an error:

Image_9217_1747236019558

When I hover on ">" arrow, I get this error:

Image_6688_1747236075932

Is this a bug or mis-usage of the feature ? 

Here is the code you can test:

import { FormsModule } from '@angular/forms'
import { GridModule, EditService, ToolbarService, GridComponent, FilterService } from '@syncfusion/ej2-angular-grids'
import { TextBoxModule } from '@syncfusion/ej2-angular-inputs'
import { AutoCompleteModule } from '@syncfusion/ej2-angular-dropdowns'
import { Component, OnInit, ViewChild } from '@angular/core';

@Component({
  selector: 'nav-tab-grid',
  template: `
    <style>
      .control-section{margin-top: 50px;}        
    </style>
    <div class="control-section">
      <div class="col-lg-9">
        <ejs-grid #normalgrid id='Normalgrid' [dataSource]='gridData'      
        [allowFiltering]="true" [filterSettings]='{ type: "Excel"}'
        >
        <e-columns>
              <e-column uid="id" field='id' headerText='ID' width='140' textAlign='Right' isPrimaryKey="true"></e-column>
              <e-column uid="name" field='name' headerText='Name' width='140' textAlign='Right'></e-column>
              <e-column uid="birthCountry" field='birthCountry.label' headerText='Birth Country' width='140' textAlign='Right'>
                <ng-template #template let-data>
                  @if (data) {                                  
                    {{ data?.birthCountry?.label }}
                  }
                </ng-template>                
              </e-column>
              <e-column uid="residenceCountry" field='residenceCountry.label' headerText='Residence Country' width='140' textAlign='Right'>
                <ng-template #template let-data>
                  @if (data) {                                  
                    {{ data?.residenceCountry?.label }}
                  }
                </ng-template>                
              </e-column>              
        </e-columns>
        </ejs-grid>
      </div>    
    </div>
  `,
  providers: [EditService, ToolbarService, FilterService],
  standalone: true,
  imports: [GridModule, FormsModule, FormsModule,
            TextBoxModule, AutoCompleteModule],  
})
export class NavTabGrid implements OnInit {

  @ViewChild(GridComponent) dataGrid!: GridComponent;  

  gridData: Object[] = []    
 
  constructor() {}

  public ngOnInit(): void {            
    this.gridData = [
      {id: 1, name: "One", birthCountry: null, residenceCountry: {label: 'Spain', code: 'SPN', id:2}},
      {id: 2, name: "Two", birthCountry: {label: 'Spain', code: 'SPN', id:2}, residenceCountry: {label: 'Spain', code: 'SPN', id:2}},
      {id: 3, name: "Three", birthCountry: {label: 'Chile', code: 'CHE', id:3}, residenceCountry: {label: 'Chile', code: 'CHE', id:3}},
      {id: 4, name: "Four", birthCountry: {label: 'Spain', code: 'SPN', id:2}, residenceCountry: {label: 'Spain', code: 'SPN', id:2}},
    ];
  }  
 }

Thanks in advance

 



DM Dineshnarasimman Muthu Syncfusion Team May 15, 2025 06:50 AM UTC

Hi,


We have reviewed you query, it appears that the first value of the Country column in your data source is null. When this happens, and the type property is not explicitly defined in the column configuration, the Grid will infer the column type based on the first available value. Since the first value is null, the column.type becomes null, which causes an error when opening the filter dialog—because the filter UI is rendered based on the column type.


To resolve this issue, we recommend explicitly setting the type property in the column definition. This ensures that the filter dialog renders correctly regardless of the initial data value


The code snippet of the implementation and sample have been attached for your reference.


      <e-column

        field="country.label"

        headerText="Country"

        width="140"

        type="string"

        textAlign="Right"

      >

 


Sample: https://stackblitz.com/edit/angular-plbv65pw?file=src%2Fapp.component.html,src%2Fapp.component.ts


Image:

 


Please get back to us for further assistance.


Regards,

Dineshnarasimman M



ID Issam DANO June 13, 2025 12:58 PM UTC

Thank you very much. Specifying the column type resolves the problem. 




DM Dineshnarasimman Muthu Syncfusion Team June 17, 2025 05:00 AM UTC

Hi,

 

Thanks for the update. We were glad that the provided suggestion resolved your issue. Please get back to us for further assistance.

 

Regards,

Dineshnarasimman M


Loader.
Up arrow icon