0
votes

I'm using handsontable, and I want to change the background color of a cell if its value is edited and changed. I can do this easily if my data source is an array of arrays (see fiddle: http://jsfiddle.net/chiman24/3o2c3c7m/).

document.addEventListener("DOMContentLoaded", function() {

    // Row Styles
    var blank = function(instance, td, row, col, prop, value,
        cellProperties) {
        Handsontable.renderers.TextRenderer.apply(this, arguments);
        td.style.backgroundColor = '#ABAAAA'
    };

    var align = function(instance, td, row, col, prop, value,
        cellProperties) {
        Handsontable.renderers.TextRenderer.apply(this, arguments);
        td.style.verticalAlign = 'middle';
        td.style.fontWeight = 'bold';
    };

    var highlight1 = function(instance, td, row, col, prop, value,
        cellProperties) {
        Handsontable.renderers.TextRenderer.apply(this, arguments);
        td.style.backgroundColor = '#BDD7EE';
        td.style.textAlign = 'right';
    };

    var changedBackgroundColor = '#cbd9e4';
    var defaultBackgroundColor = 'white';
    var hasChanged = function(instance, td, row, col, prop, value,
        cellProperties) {
        Handsontable.renderers.TextRenderer.apply(this, arguments);
        td.style.backgroundColor = changedBackgroundColor;
    };
    var noChange = function(instance, td, row, col, prop, value,
        cellProperties) {
        Handsontable.renderers.TextRenderer.apply(this, arguments);
        td.style.backgroundColor = defaultBackgroundColor;
    };

    var data = [
            ["1", "Hear us from heaven", "New Life Worship",
                "Anderson, Jared", "something"
            ],
            ["2", "Spirit Break Out", "Kim Walker", "Walker, Kim",
                "Still Believe"
            ]
        ],
        dataCopy = [
            ["1", "Hear us from heaven", "New Life Worship",
                "Anderson, Jared", "something"
            ],
            ["2", "Spirit Break Out", "Kim Walker", "Walker, Kim",
                "Still Believe"
            ]
        ],
        container = document.getElementById('example1'),
        hot1;


    //Table Row and Col Options
    hot1 = new Handsontable(container, {
        data: data,
        fixedColumnsLeft: 1,
        columnSorting: true,
        colHeaders: ["id", "title", "artist", "author", "album"],
        columns: [{
            type: "text"
        }, {
            type: "text"
        }, {
            type: "text"
        }, {
            type: "text"
        }, {
            type: "text"
        }]
    });

    hot1.addHook('afterChange', afterChange);

    function afterChange(changes, source) {
        if (source == 'edit' || source == 'autofill') {
            $.each(changes, function(index, element) {
                var change = element;
                var rowIndex = change[0];
                var columnIndex = change[1];
                var oldValue = change[2];
                var newValue = change[3];
                var cellChange = {
                    'rowIndex': rowIndex,
                    'columnIndex': columnIndex
                };
                if (oldValue != newValue) {
                    var cellProperties = hot1.getCellMeta(
                        rowIndex, columnIndex);
                    if (newValue != dataCopy[rowIndex][
                            columnIndex
                        ]) {
                        cellProperties.renderer = hasChanged;
                    } else { //data changed back to original value.
                        cellProperties.renderer = noChange;
                    }
                    hot1.render();
                }
            });
        }
    }
});


// noSideScroll class added to fix some containers while side scrolling the table
$(window).scroll(function() {
    $('.noSideScroll').css({
        'left': $(this).scrollLeft()
    });
});

However, when using an array of objects, I can't get it to work. (see fiddle: http://jsfiddle.net/chiman24/24mpavga/).

var data = [{
        "id": 1,
        "title": "First Loved Me",
        "artist": "Israel and New Breed",
        "author": "Houghton, Israel",
        "album": "Covered:  Alive In Asia"
    }, {
        "id": 2,
        "title": "One Thing Remains",
        "artist": "Israel and New Breed",
        "author": "Houghton, Israel",
        "album": "Covered:  Alive In Asia"
    }],
    dataCopy = [{
        "id": 1,
        "title": "First Loved Me",
        "artist": "Israel and New Breed",
        "author": "Houghton, Israel",
        "album": "Covered:  Alive In Asia"
    }, {
        "id": 2,
        "title": "One Thing Remains",
        "artist": "Israel and New Breed",
        "author": "Houghton, Israel",
        "album": "Covered:  Alive In Asia"
    }],
    container = document.getElementById('example1'),
    hot1;


//Table Row and Col Options
hot1 = new Handsontable(container, {
    data: data,
    fixedColumnsLeft: 1,
    columnSorting: true,
    colHeaders: ["id", "title", "artist", "author", "album"],
    columns: [{
        data: "id"
    }, {
        data: "title"
    }, {
        data: "artist"
    }, {
        data: "author"
    }, {
        data: "album"
    }]
});

hot1.addHook('afterChange', afterChange);

function afterChange(changes, source) {
    if (source == 'edit' || source == 'autofill') {
        $.each(changes, function(index, element) {
            var change = element;
            var rowIndex = change[0];
            var columnIndex = change[1];
            var oldValue = change[2];
            var newValue = change[3];
            var cellChange = {
                'rowIndex': rowIndex,
                'columnIndex': columnIndex
            };
            if (oldValue != newValue) {
                var cellProperties = hot1.getCellMeta(
                    rowIndex, columnIndex);
                if (newValue != dataCopy[rowIndex][
                        columnIndex
                    ]) {
                    cellProperties.renderer = hasChanged;
                } else { //data changed back to original value.
                    cellProperties.renderer = noChange;
                }
                hot1.render();
            }
        });
    }
}

Is there a way to accomplish this? I want to get it working using an array of objects because my data coming from the server will be in JSON format. I've scoured the handsontable documentation for a couple of days to no avail. Any help will be much appreciated. Thanks.

2
Very interesting issue. One thing you're missing is that you should be returning the td on your custom renderers. Won't change the outcome unfortunately. - ZekeDroid
As a workaround, you could keep a map of all cells that are changed, and have a generic renderer which checks for the current cell to be in this map. If yes, set one color, otherwise use the default color. - ZekeDroid
@ZekeDroid Thanks for looking into it. I notice that using {data: "id"}, {data: "title}, etc causes issues as far as the cellProperties object that's returned from "getCellMeta". When using the "data" attribute as shown, cellProperties.renderer is "undefined". It could be a handsontable bug. - chionw
well, you're supposed to set it using setCellMetaObject otherwise it doesn't do anything. But even with that set, something isn't working. You can post it on the github issues page, maybe it is a bug - ZekeDroid

2 Answers

0
votes

I got some help from the handsontable github forum.

Apparently, if the data source is an array of objects, then when calling "getCellMeta", instead of passing in a numerical column index, you have to pass in the column index as a property like this:

hot.getCellMeta(2, hot.propToCol(columnIndex));

Here's the updated demo

0
votes

Other way to change background color of cell is use the Cell Option

...
if (oldValue != newValue){

    aCell.push(
        { row: rowIndex, 
          col: hot.propToCol(columnIndex), 
          className: "cssWithBackgroundColor" });

    hot.updateSettings({ cell: aCell });
}

If the user undo change you can

if ( source == 'UndoRedo.undo'){
    aCell.pop();
    hot.updateSettings({ cell: aCell });
}