How to save an instance of cellMeta and apply it to another instance of handsontable?

Tags: #<Tag:0x00007f135f611d08> #<Tag:0x00007f135f611bc8>
async applyBackgroundColor(color: string): Promise<void> {

        this.backgroundColor = color;

        return new Promise<void>((resolve, reject) => {

            try {

                this.selected.forEach(selection => {

                    Object.keys(selection).map(rowIndex => {

                        const selectedRow = selection[parseInt(rowIndex)];

                        console.log('row selected:', rowIndex);

                        selectedRow.forEach(async cell => {

                            const [row1, col1, value1] = cell;

                            const cellMeta = this.handsontableInstance.getCellMeta(row1, col1);

                            const cellRenderer = this.handsontableInstance.getCellRenderer(cellMeta);

                            // const cellStyles = window.getComputedStyle(this.handsontableInstance.getCell(row1,col1)!);

                            // console.log(cellStyles.background);

                            const newRenderer = function (instance: any, td: any, row2: any, col: any, prop: any, value: any, cellProperties: any) {

                                if (typeof cellRenderer === 'function') {

                                    cellRenderer(instance, td, row2, col, prop, value, cellProperties);

                                    td.style.background = color;

                                }

                            }

                            this.setCustomRenderer(row1, col1, newRenderer, this.handsontableInstance);

                            if (this.activeSheet.rows) {

                                const cell = this.activeSheet.rows[row1].cells.find(m => m.row === row1 && m.col === col1);

                                if (cell && cell.cellStyles.fill && cell.cellStyles.fill.bgColor) {

                                    cell.cellStyles.fill.bgColor.argb = color;

                                }

                            }

Then, I filter by the cells that have renderer applied

public saveJSON() {

        // GET Handsontable DATA

        const data = this.hot.getData();

        const cellsWithValue = data.filter((row: any[]) => {

            return row.some(cell => cell !== null && cell !== undefined && cell !== '');

        });

        // GET HOT Configuration

        const settings = this.hot.getSettings();

        const meta = this.hot.getCellsMeta();

        const cellsWithRenderer = meta.filter((cellMeta: Handsontable.CellProperties) => {

            return cellMeta.hasOwnProperty('renderer');

        });

   

        // Modify meta elements to include the renderer directly

        const modifiedMeta = cellsWithRenderer.map((cellMeta: Handsontable.CellProperties) => {

            return {

                ...cellMeta,

                renderer: cellMeta.renderer?.toString() // Convert the renderer function to a text string to be exported as JSON

            };

        });

   

        console.log('values',cellsWithValue);

        console.log('renderers',cellsWithRenderer);

        console.log('settings',settings);

   

        // Combine data and configuration into a JSON object

        const jsonData = {

            name: this.activeSheet.name,

            data: cellsWithValue,

            meta: modifiedMeta // Include renderer function definitions directly in each meta element

        };

   

        // Convert the JSON object to a string

        const jsonString = JSON.stringify(jsonData);

   

        this.sheetService.saveTemplate(jsonString).subscribe((response: any) => {

   

        });

   

        //UNCOMMENT THIS IF YOU WANT TO CREATE A DOWNLOADABLE FILE

        // this.fileService.saveJsonToFile(jsonString, fileName)

    }

This generates a JSON with the cell data and the applied renderers. For example:

  1. 0: ColumnMeta2

  2. col: 0

  3. prop: 0

  4. renderer: ƒ (instance, td, row2, col, prop, value, cellProperties)

  5. row: 0

  6. visualCol: 0

  7. visualRow: 0

  8. fixedColumnsLeft: (…)

  9. fixedColumnsStart: (…)

  10. layoutDirection: (…)

  11. [[Prototype]]: TableMeta

  12. length: 1

  13. [[Prototype]]: Array(0)

In another instance where I try to obtain this JSON to create an instance of Handsontable with the loaded template I am using:

const data = JSON.parse(this.templates[0].jsonData);

const settings = {

        data: data.Data, // Use the data provided in the JSON

        meta: [] as Handsontable.CellMeta[], // Defines the columns based on the JSON data

      };

this.hot = new Handsontable(mainElement, {

        minRows: 50,

        minCols: 8,

        width: 'auto',

        licenseKey: 'non-commercial-and-evaluation',

      });

if(settings.data.length > 0){

    this.hot.loadData(settings.data);

  }

settings.meta.map((meta: any) => {

    console.log(meta);

    const rendererFunction = eval(`(${meta.renderer})`);

    this.hot.setCellMetaObject(meta.row,meta.col,{ renderer: rendererFunction });

   

    console.log('after apply render: ', this.hot.getCellMeta(meta.row,meta.col));

    this.hot.render();

  })

Any suggestions?
Basically what I’m trying to do is:

  1. define a template using handsontable,
  2. In another instance, be able to load that template, with data and styles

When creating the template, the render works excellent, but when “exporting” it and loading it in another instance it stops working

Hi @joaquinnicolasm

It will be easier to investigate this problem if you could provide a working demo in a chosen sandbox. Can you please prepare one?

Hello @joaquinnicolasm,

It seems like you’re working with Handsontable, trying to create a template with data and styles, and then load that template in another instance. Let’s break down the issue and explore potential solutions

When you export the template, you’re serializing the renderer function as a string in the JSON.
However, when you load the template in another instance, you need to convert that string back into a function.
The eval approach you’re using might not work consistently due to potential security risks and limitations.
Instead, consider using a more robust method to deserialize the renderer function.

When loading the template, you can parse the renderer function from the string representation.
One way to achieve this is by using Function constructor to create a function from the serialized string.

Make sure that the renderer function you’re exporting and the one you’re importing are consistent.
Check if any other settings or configurations might be affecting the rendering behavior.

Inspect the console logs during the import process to identify any errors or unexpected behavior.
Verify that the renderer function is correctly deserialized and applied to the cells.

Create a minimal test case with a simple renderer function and verify if it works as expected during import.
Gradually add complexity (e.g., additional styling, custom logic) to narrow down the issue.

Best Regards,

:+1: for this advice. Simplifying trying when something doesn’t work is the first thing we have to do.