Angular Data GridBinding to data

Fill your data grid with various data structures, including an array of arrays or an array of objects.

Compatible data types

Array of arrays

Array of arrays is a good choice for the more grid-like scenarios where you need to provide the end user with permission to manipulate the grid, e.g., insert columns, delete rows, decorate cells, etc.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';

@Component({
  selector: 'example1-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="data" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example1BindingDataComponent {

  readonly data: Array<Array<string | number>> = [
    ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
    ['2017', 10, 11, 12, 13, 15, 16],
    ['2018', 10, 11, 12, 13, 15, 16],
    ['2019', 10, 11, 12, 13, 15, 16],
    ['2020', 10, 11, 12, 13, 15, 16],
    ['2021', 10, 11, 12, 13, 15, 16],
  ];

  readonly gridSettings: GridSettings = {
    startRows: 5,
    startCols: 5,
    colHeaders: true,
    height: 'auto',
    width: 'auto',
    autoWrapRow: true,
    minSpareRows: 1,
    autoWrapCol: true
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example1BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example1BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example1BindingDataComponent ]
})

export class AppModule { }

Array of arrays with a selective display of columns

The following example shows how you would use the array of arrays with a selective display of columns. This scenario uses the same data source as in the previous example, this time omitting the Tesla column from the grid.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';

@Component({
  selector: 'example2-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="data" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example2BindingDataComponent {

  readonly data: Array<Array<string | number>> = [
    ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
    ['2017', 10, 11, 12, 13, 15, 16],
    ['2018', 10, 11, 12, 13, 15, 16],
    ['2019', 10, 11, 12, 13, 15, 16],
    ['2020', 10, 11, 12, 13, 15, 16],
    ['2021', 10, 11, 12, 13, 15, 16],
  ];

  readonly gridSettings: GridSettings = {
    colHeaders: true,
    minSpareRows: 1,
    height: 'auto',
    width: 'auto',
    columns: [
      { data: 0 },
      // skip the second column
      { data: 2 },
      { data: 3 },
      { data: 4 },
      { data: 5 },
      { data: 6 },
    ],
    autoWrapRow: true,
    autoWrapCol: true
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example2BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example2BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example2BindingDataComponent ]
})

export class AppModule { }

Array of objects

An array of objects can be used as a data source as follows:

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';

interface Person {
  id: number;
  name: string;
  address: string;
}

@Component({
  selector: 'example3-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="data" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example3BindingDataComponent {

  readonly data: Person[] = [
    { id: 1, name: 'Ted Right', address: '' },
    { id: 2, name: 'Frank Honest', address: '' },
    { id: 3, name: 'Joan Well', address: '' },
    { id: 4, name: 'Gail Polite', address: '' },
    { id: 5, name: 'Michael Fair', address: '' },
  ];

  readonly gridSettings: GridSettings = {
    colHeaders: true,
    height: 'auto',
    width: 'auto',
    minSpareRows: 1,
    autoWrapRow: true,
    autoWrapCol: true
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example3BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example3BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example3BindingDataComponent ]
})

export class AppModule { }

Array of objects with column as a function

You can set the columns configuration option to a function. This is good practice when you want to bind data more dynamically.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';

interface Person {
  id: number;
  name?: { first: string; last: string };
  address: string;
}

@Component({
  selector: 'example4-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="data" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example4BindingDataComponent {

  readonly data: Person[] = [
    { id: 1, name: { first: 'Ted', last: 'Right' }, address: '' },
    { id: 2, address: '' }, // Handsontable will create missing properties on demand
    { id: 3, name: { first: 'Joan', last: 'Well' }, address: '' },
  ];

  readonly gridSettings: GridSettings = {
    colHeaders: true,
    height: 'auto',
    width: 'auto',
    columns: (columnIndex: number) => {
      switch (columnIndex) {
        case 0:
          return { data: 'id' };
        case 1:
          return { data: 'name.first' };
        case 2:
          return { data: 'name.last' };
        case 3:
          return { data: 'address' };
        default:
          return {};
      }
    },
    minSpareRows: 1,
    autoWrapRow: true,
    autoWrapCol: true
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example4BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example4BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example4BindingDataComponent ]
})

export class AppModule { }

Array of objects with column mapping

In a scenario where you have nested objects, you can use them as the data source by mapping the columns using the columns option.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';

interface Person {
  id: number;
  name?: { first: string; last: string };
  address: string;
}

@Component({
  selector: 'example5-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="data" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example5BindingDataComponent {

  readonly data: Person[] = [
    { id: 1, name: { first: 'Ted', last: 'Right' }, address: '' },
    { id: 2, address: '' }, // Handsontable will create missing properties on demand
    { id: 3, name: { first: 'Joan', last: 'Well' }, address: '' },
  ];

  readonly gridSettings: GridSettings = {
    colHeaders: true,
    height: 'auto',
    width: 'auto',
    columns: [
      { data: 'id' },
      { data: 'name.first' },
      { data: 'name.last' },
      { data: 'address' },
    ],
    minSpareRows: 1,
    autoWrapRow: true,
    autoWrapCol: true
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example5BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example5BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example5BindingDataComponent ]
})

export class AppModule { }

Array of objects with custom data schema

When using object data binding, Handsontable needs to know what data structure to create when adding a new row. If your data source contains at least one row, Handsontable will figure out the data structure based on the first row.

In a scenario where you start with an empty data source, you will need to provide the dataSchema option containing the data structure for any new row added to the grid. The example below shows a custom data schema with an empty data source:

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';

@Component({
  selector: 'example6-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="data" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example6BindingDataComponent {

  readonly data = [];
  readonly gridSettings: GridSettings = {
    dataSchema: {
      id: null,
      name: {
        first: null,
        last: null,
      },
      address: null,
    },
    startRows: 5,
    startCols: 4,
    colHeaders: ['ID', 'First Name', 'Last Name', 'Address'],
    height: 'auto',
    width: 'auto',
    columns: [
      { data: 'id' },
      { data: 'name.first' },
      { data: 'name.last' },
      { data: 'address' },
    ],
    minSpareRows: 1,
    autoWrapRow: true,
    autoWrapCol: true
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example6BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example6BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example6BindingDataComponent ]
})

export class AppModule { }

Function data source and schema

If your dataSchema is a constructor of an object that doesn't directly expose its members, you can specify functions for the data member of each columns item.

The example below shows how to use such objects:

/* file: app.component.ts */
import { Component, Type } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';
import Handsontable from 'handsontable';

interface Person {
  id: number | undefined;
  name: string | undefined;
  address: string | undefined;
  attr: (attr: string, val?: Handsontable.CellValue) => keyof Person | Person;
}

interface ModelOptions {
  [x: string]: any;
  id?: number;
  name?: string;
  address?: string;
  hasOwnProperty?: (prop: string) => boolean;
}

type PrivPerson = {
  [K in keyof Person]: Person[K];
} & { [key: string]: any };

function model(opts: ModelOptions): Partial<Person> {
  const _pub: Partial<Person> = {
    id: undefined,
    name: undefined,
    address: undefined,
    attr: undefined,
  };

  const _priv: Partial<PrivPerson> = {};

  for (const i in opts) {
    if (opts.hasOwnProperty && opts.hasOwnProperty(i)) {
      _priv[i] = opts[i];
    }
  }

  _pub.attr = function (
    attr: keyof Person | string,
    val?: Handsontable.CellValue
  ) {
    if (typeof val === 'undefined') {
      window.console && console.log('GET the', attr, 'value of', _pub);

      return _priv[attr];
    }

    window.console && console.log('SET the', attr, 'value of', _pub);
    _priv[attr] = val;

    return _pub;
  };

  return _pub;
}

function property(attr: keyof Person | string) {
  return (row: Handsontable.RowObject, value?: Handsontable.CellValue) =>
    (row as Person).attr(attr, value);
}

@Component({
  selector: 'example7-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="data" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example7BindingDataComponent {

  readonly data = [
    model({ id: 1, name: 'Ted Right', address: '' }),
    model({ id: 2, name: 'Frank Honest', address: '' }),
    model({ id: 3, name: 'Joan Well', address: '' }),
    model({ id: 4, name: 'Gail Polite', address: '' }),
    model({ id: 5, name: 'Michael Fair', address: '' }),
  ];

  readonly gridSettings: GridSettings = {
    dataSchema: model,
    height: 'auto',
    width: 'auto',
    colHeaders: ['ID', 'Name', 'Address'],
    columns: [
      { data: property('id') },
      { data: property('name') },
      { data: property('address') },
    ],
    minSpareRows: 1,
    autoWrapRow: true,
    autoWrapCol: true
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example7BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example7BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example7BindingDataComponent ]
})

export class AppModule { }

No data

By default, if you don't provide any data, Handsontable renders as an empty 5x5 grid.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';

@Component({
  selector: 'example9-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example9BindingDataComponent {

  readonly gridSettings: GridSettings = {
    autoWrapRow: true,
    autoWrapCol: true,
    height: 'auto'
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example9BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example9BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example9BindingDataComponent ]
})

export class AppModule { }

To change the number of rows or columns rendered by default, use the startRows and startCols options.

Data-manipulating API methods

Understand binding as a reference

Handsontable binds to your data source by reference, not by values. We don't copy the input dataset, and we rely on JavaScript to handle the objects. Any data entered into the grid will alter the original data source.

TIP

Handsontable initializes the source data for the table using a reference, but you shouldn't rely on it. For example, you shouldn't change values in the source data using the reference to the input dataset. Some mechanisms for handling data aren't prepared for external changes that are made in this way.

To avoid this scenario, copy the data before you pass it to the grid. To change the data from outside Handsontable, you can use our API methods. For example, a change being made will be displayed immediately on the screen after calling the setDataAtCell() method.

/* file: app.component.ts */
import { Component, ViewChild, AfterViewInit } from '@angular/core';
import { GridSettings, HotTableComponent } from '@handsontable/angular-wrapper';

@Component({
  selector: 'example10-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="data" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example10BindingDataComponent implements AfterViewInit {
  @ViewChild(HotTableComponent, { static: false }) readonly hotTable!: HotTableComponent;

  readonly data: Array<Array<string | number>> = [
    ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
    ['2017', 10, 11, 12, 13, 15, 16],
    ['2018', 10, 11, 12, 13, 15, 16],
    ['2019', 10, 11, 12, 13, 15, 16],
    ['2020', 10, 11, 12, 13, 15, 16],
    ['2021', 10, 11, 12, 13, 15, 16],
  ];

  readonly gridSettings: GridSettings = {
    height: 'auto',
    autoWrapRow: true,
    autoWrapCol: true
  };

  ngAfterViewInit(): void {
    this.hotTable?.hotInstance?.setDataAtCell(0, 1, 'Ford');
  }
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example10BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example10BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example10BindingDataComponent ]
})

export class AppModule { }

There are multiple ways you can insert your data into Handsontable. Let's go through the most useful ones:

The data configuration option

You will probably want to initialize the table with some data (if you don't, the table will render an empty 5x5 grid for you). The easiest way to do it is by passing your data array as the value of HotTable's data @Input():

import { GridSettings } from "@handsontable/angular-wrapper";

data = newDatset,
gridSettings: GridSettings = {};
<hot-table [data]="data" [settings]="gridSettings" />

The data-loading API methods

TIP

To use the Handsontable API, you'll need access to the Handsontable instance. You can do that by utilizing a reference to the HotTableComponent, and reading its hotInstance property.

For more information, see the Instance access page.

To replace the entire data in an already-initialized Handsontable instance, you can use one of the data-loading API methods:

  • loadData()
    Replaces the data used in Handsontable with the dataset provided as the method argument.
    Note: Since version 12.0.0 this method causes the table to reset its configuration options and index mapper information, so some of the work done on the table since its initialization might be lost.
    hot.loadData(newDataset);
    
  • updateData()
    Replaces the data used in Handsontable with the dataset provided as the method argument. Unlike loadData(), updateData() does NOT reset the configuration options and/or index mapper information, so it can be safely used to replace just the data, leaving the rest of the table intact.
    hot.updateData(newDataset);
    
  • updateSettings()
    Updates the configuration of the table, updateSettings() can be also used to replace the data being used. Since version 12.0.0, under the hood it utilizes the updateData() method to perform the data replacement (apart from the one automatic call done during the initialization, where it uses loadData()).
    hot.updateSettings({
      data: newDataset,
      // ... other config options
    });
    

You can also use the built-in mechanism of the Angular wrapper to update data. When a change in the @Input data is detected by ngOnChanges, the wrapper will call hot.updateData(newDataset); with the data provided in the @Input.

The data-modifying API methods

To modify just a subset of data passed to Handsontable, these are the methods you might want to check out:

  • setDataAtCell()
    Replaces data in a single cell or to perform a series of single-cell data replacements:

    // Replaces the cell contents at the (0, 2) visual coordinates (0 being the visual row index, 2 - the visual column index) with the supplied value.
    hot.setDataAtCell(0, 2, 'New Value');
    
    // Replaces the cells at `(0,2)`, `(1,2)` and `(2,2)` with the provided values.
    const changes = [
      [0, 2, 'New Value'],
      [1, 2, 'Different Value'],
      [2, 2, 'Third Replaced Value'],
    ];
    hot.setDataAtCell(changes);
    
  • setDataAtRowProp()
    Replaces data in a single cell or to perform a series of single-cell data replacements, analogously to setDataAtCell(), but allows targeting the cells by the visual row index and data row property. Useful for the Array of objects data type.

    // Replaces the cell contents at the (0, 'title') coordinates (0 being the visual row index, 'title' - the data row object property) with the supplied value.
    hot.setDataAtRowProp(0, 'title', 'New Value');
    
    // Replaces the cells with the props of 'id', 'firstName' and 'lastName' in the first row with the provided values.
    const changes = [
      [0, 'id', '22'],
      [0, 'firstName', 'John'],
      [0, 'lastName', 'Doe'],
    ];
    hot.setDataAtRowProp(changes);
    
  • setSourceDataAtCell()
    As the displayed data coordinates can differ from the way it's stored internally, sometimes you might need to target the cells more directly - that's when setSourceDataAtCell() comes in handy. The row and columns/prop arguments represent the physical indexes.

    // Replaces the cell contents at the (0, 2) coordinates (0 being the physical row index, 2 - the physical column index) with the supplied value.
    hot.setSourceDataAtCell(0, 2, 'New Value');
    
    // Replaces the cell contents at the (0, 'title') coordinates (0 being the physical row index, 'title' - the data row property) with the supplied value.
    hot.setSourceDataAtCell(0, 'title', 'New Value');
    
    // Replaces the cells with the props of 'id', 'firstName' and 'lastName' in the first physical row with the provided values.
    const changes = [
      [0, 'id', '22'],
      [0, 'firstName', 'John'],
      [0, 'lastName', 'Doe'],
    ];
    hot.setSourceDataAtCell(changes);
    
  • populateFromArray()
    Replaces a chunk of the dataset by provided the start (and optionally end) coordinates and a two-dimensional data array of new values.

    TIP

    The populateFromArray() method can't change read-only cells.

    const newValues = [
      ['A', 'B', 'C'],
      ['D', 'E', 'F']
    ];
    
    // Replaces the values from (1, 1) to (2, 3) visual cell coordinates with the values from the `newValues` array.
    hot.populateFromArray(1, 1, newValues);
    
    // Replaces the values from (1, 1) to (2, 2) visual cell coordinates with the values from the `newValues` array, ommiting the values that would fall outside of the defined range.
    hot.populateFromArray(1, 1, newValues, 2, 2);
    

Working with a copy of data

When working with a copy of data for Handsontable, it is best practice is to clone the data source before loading it into Handsontable. This can be done with structuredClone(data) or legacy JSON.parse(JSON.stringify(data)) or another deep-cloning function.

/* file: app.component.ts */
import { Component } from '@angular/core';
import { GridSettings } from '@handsontable/angular-wrapper';

@Component({
  selector: 'example11-binding-data',
  standalone: false,
  template: ` <div>
    <hot-table [data]="dataAfterClone" [settings]="gridSettings"></hot-table>
  </div>`,
})
export class Example11BindingDataComponent {

  readonly data: Array<Array<string | number>> = [
    ['', 'Tesla', 'Nissan', 'Toyota', 'Honda', 'Mazda', 'Ford'],
    ['2017', 10, 11, 12, 13, 15, 16],
    ['2018', 10, 11, 12, 13, 15, 16],
    ['2019', 10, 11, 12, 13, 15, 16],
    ['2020', 10, 11, 12, 13, 15, 16],
    ['2021', 10, 11, 12, 13, 15, 16],
  ];
  readonly dataAfterClone = structuredClone(this.data);

  readonly gridSettings: GridSettings = {
    height: 'auto',
    autoWrapRow: true,
    autoWrapCol: true
  };
}



/* file: app.module.ts */
import { NgModule, ApplicationConfig } from '@angular/core';
import { BrowserModule } from '@angular/platform-browser';
import { registerAllModules } from 'handsontable/registry';
import { HOT_GLOBAL_CONFIG, HotGlobalConfig, HotTableModule } from '@handsontable/angular-wrapper';
import { CommonModule } from '@angular/common';
import { NON_COMMERCIAL_LICENSE } from '@handsontable/angular-wrapper';
import { Example11BindingDataComponent } from './app.component';

// register Handsontable's modules
registerAllModules();

export const appConfig: ApplicationConfig = {
  providers: [
    {
      provide: HOT_GLOBAL_CONFIG,
      useValue: {
        themeName: 'ht-theme-main',
        license: NON_COMMERCIAL_LICENSE,
      } as HotGlobalConfig
    }
  ],
};

@NgModule({
  imports: [ BrowserModule, HotTableModule, CommonModule ],
  declarations: [ Example11BindingDataComponent ],
  providers: [...appConfig.providers],
  bootstrap: [ Example11BindingDataComponent ]
})

export class AppModule { }