[GH #6274] Best place to set cell meta data?

Tags: #<Tag:0x00007fbe90414dc0> #<Tag:0x00007fbe9041b1c0>

Hi!

First, thanks for the awesome library, it works like a charm for me :slightly_smiling_face:

My case: I have complexe javascript objects that serve as row data. Each column corresponds to an attribute in these objects.

// A row object example
const myFirstRow = {
     header1: { someProp: true, otherProp: 123, value: 'hello world' },
     header2: { someProp: false, otherProp: 0, value: 'goodbye' },
}

// columns option looks like this. The code is simplified for clarity.
columns: [{
    data: `header1.value`,
},
{
    data: `header2.value`,
}]

The value attribute in my columns is the property I expect to be rendered in the cells. It works great this way.

But I want my other properties (like someProp: true, otherProp: 123) to be part of the cell properties (meta data).

What I’ve tried so far:

  • Currently, I’m using the cells hook with a function to set it up. I use getSourceDataAtRow() to get my entire object for the row (myFirstRow), then get the corresponding column (header1) within it and return that in the cells function. It works, but this hook is called whenever a cell is rendered, thus its metadata resets everytime!
  • I want it to be set once for all at init for example, but by using such hooks, I have to manually iterate through all my columns + rows to get the data and call setCellMetaObject for each cell. It’s costly and I feel like it’s not the proper way to do this.

Do you have any idea to accomplish this in a clean way?

Welcome on our forum @florent.ct

here are my first approaches https://jsfiddle.net/4qsdej25/ but I’m not sure if that’s what you wanted to get.

Hey Aleksandra!

Thank for your quick reply, I appreciate :slight_smile:

That’s the solution I came up with as well (using the cells settings). But as I said, this hook is called whenever cells are rendered, so the cells metadata are actually reset at every call of this function.

This means that if some renderer edits the cell metadata, the cells function will then override it with the objects (myFirstRow).
I managed to use the afterSetCellMeta hook to actually update my source data (which is used to set my cell meta data, remember) from any changes in the meta data. It kind of mimic a double ways binding between myFirstRow properties and my cell meta data.

Nevertheless, it feels wrong this way, like I copy some data to handsontable, then when handsontable updates its metadata, I copy it to my source data…
It’s working, so if you don’t have a better solution, I’ll stick with it!

The setCellMeta() method will add the meta once and keep it once cells are moved to other position and won’t change it on render - https://jsfiddle.net/L47es9ja/ But you already mentioned setCellMetaObject being to costly. We do not have one method to set meta for all the cells.

Ok I reproduced what I meant on a jsfiddle: https://jsfiddle.net/Kapcash/odk15r7f/20/

Here, my cells becomes pink after editing them, because my custom editor sets a cell metadata manually. By the last cell should already be pink because my source object already has “edited === true”. This doesn’t work because my cells metadata are not initialized from my source object.

To avoid that, I tried using “cells” to correctly initialize my cells metadata. It works well, but now it resets my cell metadata at every call, so it doesn’t work anymore…

As I said, my solution was to listen to afterSetCellMeta events, so that I also update my source data (myFirstRow). But I wonder if you can think of a cleaner way to do that? (i.e. avoid “cells” to reset my metadata everytime!

Thank you for your patience while helping me :slightly_smiling_face:

Yes, that’s true. The cells method runs each time we force a render (changing values, sort, filter, move rows, etc). If you want to pass some metadata it would need to be via setCellMeta (/object). You may add a condition that checks if getCellMeta(row, column, edited, true) and if the reply is true do not change the edited state. But it doesn’t change the fact that cells method will get called wth every render.

ps. you may need to push cells to the instance.updateSettings() to be called after all other code executed (with setCellMeta).

Thanks for your reply :slight_smile:

I’ll stick to this solution then!

Maybe you could consider adding a specific hook to initialize the cells metadata once from the source data? I feel like there is something you can improve on that!

PS: You can close the thread if you want, I had my answers :wink:

I’m the one who always requests for a new hook but in this case, I do not know how we should trigger that. I guess that first there should be a method, then we’d be able to think about a hook. maybe something like setCellsMeta.

I get it! So we could call this method in the init hook actually.

The perfect approach for me, would be a hook just like cells but called only once for each cell like that:

initCellMeta: function(row, col, prop, sourceRowData) {
    // Get the object corresponding to my column from the prop `header1.value`
    const cellRowData = sourceRowData[prop.replace('.value', '')]
    return {
       id: sourceData.id,
       saved: sourceData.saved,
       tooltip: sourceData.description,
    }
}

The return object may be merged to the cellProperties.

Another approach could be:

data: [{
   header1: { value: 'hello', id: 1, saved: false }
   header2: { value: 'goodbye', id: 2, saved: true }
}],
columns: [{
    data: 'header1.value',
    cellsMeta: 'header1', // path to a property in the sourceData row that contains all properties to copy to the cellProperties
}]

And while initialising the table, the value become sourceDataRow1.header1.value, and its metadata are copied from sourceDataRow1.header1 (except for .value because it’s already used for the cell value itself)

Anyway, I know it’s more complicated than that to build a new feature like this. I just believe it’s a real need for us, users, and wanted to give a feedback on that :slight_smile:

1 Like

I like those ideas :slight_smile: I’ve checked what we have on Github and found out that one of our refactors mentioned

add setRowMeta , getRowMeta methods #4901 (we can use indexMapper to store them)

src https://github.com/handsontable/handsontable/issues/6274

so that is pretty the same approach as the one you’ve suggested.