Conditional formatting : styles defined in JavaScript or CSS?

Tags: #<Tag:0x00007f8b1ddf0188>

Hello,

I’m a bit struggling with the rendering of the cells in my HOT instance. :confused:

Purpose is to apply a specific style (backgroundColor, color, fontWeight, fontStyle) to every cells in column A based on their value. If the value has no dedicated style, just leave it as is. There’s approximately 100 different styles.

My current solution is to define a big array of Objects (each Object represent a style) in a custom renderer JavaScript file, and to handle the logic in a function like in the Conditional formatting demo.

Here’s a snippet of my code :

const STYLES = [
  {
    CellValue: 'Input',
    FillType: 'ROW',
    CellStyle: {
      backgroundColor: 'rgb(255, 153, 0)',
      color: 'black',
      fontWeight: 'bold',
      fontStyle: 'normal'
    }
  },
  {
    CellValue: 'EndInput',
    FillType: 'ROW',
    CellStyle: {
      backgroundColor: 'rgb(255, 153, 0)',
      color: 'black',
      fontWeight: 'bold',
      fontStyle: 'normal'
    }
  },
  {
    CellValue: 'Version Number',
    FillType: 'CELL',
    CellStyle: {
      backgroundColor: 'rgb(192, 192, 192)',
      color: 'black',
      fontWeight: 'bold',
      fontStyle: 'normal'
}
},
  {
    CellValue: 'InsertMessage',
    FillType: 'CELL',
    CellStyle: {
      backgroundColor: 'rgb(196, 215, 155)',
      color: 'black',
      fontWeight: 'bold',
      fontStyle: 'normal'
    }
  },

export default function MyCustomRenderer(instance, td, row, col, prop, value, cellProperties) {

  // Do not forget to set the value of the cell
  td.innerHTML = value;

  // General style
  if (value && value.length > 0) {
    td.style.borderBottom = 'thin solid #000000';
    td.style.borderRight = 'thin solid #000000';
  }

  td.style.color = 'black';
  td.style.fontFamily = 'Calibri, sans-serif';
  td.style.fontSize = '0.8rem';

  if (col === 0) {
    console.log(`Cell [${row},${col}] is going to be rendered.`);

    // Specific rendering for cells in column A
    const columnAStyle = STYLES.find(o => o.CellValue === value);

    if (typeof columnAStyle !== 'undefined') {
        console.log(`Value [${value}] has a specific style.`);

        // Cell value corresponds to the start/end of a function/section
        td.style.backgroundColor = columnAStyle.CellStyle.backgroundColor;
        td.style.color = columnAStyle.CellStyle.color;
        td.style.fontWeight = columnAStyle.CellStyle.fontWeight;
        td.style.fontStyle = columnAStyle.CellStyle.fontStyle;
    }
  }

  return td;
}

Is it the right way to do it ? Or is it better to move all these styles in a css file and call the different classnames ?

Thanks a lot for your help,
Caroline

Hey Caroline!

In Handsontable styling for cells needs to be done by className. Only then you’ll be able to set the values once and keep them after table rerenders.

Do those settings change over time or depend on the cell’s content?

Hi Aleksandra,

Thanks for your reply. :smile:

Ok, I’m now moving all the styles from the array to the css file then.

Settings only rely on the cells content. For instance, if cell[15,0] = ‘Input’, then the cell must be filled in orange (RGB 255,153,0) with a normal bold black font.

I have also to change the logic and try to make it generic (like a direct match between the classname and the cell content).

I’m wondering what’s the best practice : defining a function for cells in the settings, or still using my own renderer (cellProperties are still available anyway) ?

A custom renderer works, but it is slower on the long run. As far as you don’t need to change the content cells method is the best one to pick.

1 Like

Hello Aleksandra,

Hope you’re fine. :slight_smile:

I have a very basic (and probably stupid) question about the way to get a cell value from the cells option…

Following code throws a Uncaught TypeError: this.getSourceDataAtCell is not a function error :

export default class EditorTable extends React.PureComponent {
  constructor(props) {
    super(props);

    this.hotTableId = `hot-${props.scenario.name}`;

    this.hotTableSettings = {
      colHeaders: true,
      rowHeaders: true,
      manualColumnResize: true,
      manualRowResize: true,
      manualColumnMove: true,
      manualRowMove: true,
      cells: function (row, col, prop) {
        const cellProperties = {};

        console.log(`Cell [${row},${col}] has value = [${this.getSourceDataAtCell(row, col)}]`
        );

        return cellProperties;
      },
      // renderer: MyCustomRenderer,

Thanks again.
Kind regards,
Caroline

I solved my problem by using a callback function (getCellsStyle in this example) :

import getCellsStyle from '../styles/CellStyle';

export default class EditorTable extends React.PureComponent {
  constructor(props) {
    super(props);

    this.hotTableId = `hot-${props.scenario.name}`;

    this.hotTableSettings = {
      colHeaders: true,
      rowHeaders: true,
      manualColumnResize: true,
      manualRowResize: true,
      manualColumnMove: true,
      manualRowMove: true,
      cells: getCellsStyle,
      // renderer: MyCustomRenderer,

and define this function in a separated file (CellStyle.js in this example) :

// @flow

export default function getCellsStyle(row, col, prop) {
  const cellProperties = {};

  if (col === 0) {
    const cellValue = this.instance.getSourceDataAtCell(row, col);

    if (cellValue) {
      let calculatedClassName = cellValue.replace(/([a-z])([A-Z])/g, "$1-$2").toLowerCase();
      calculatedClassName = calculatedClassName.replace(/\s+/g, '-');

      cellProperties.className = calculatedClassName;
    }
  }

  return cellProperties;
}

In my css file, all the class names are based on the cell content. The replace methods above are just for replacing the camel case with kebab case.

@aleksandra_budnik : you can close this topic now. Thanks for your help. :wink:

Glad you got it working, Caroline.

Feel free to open a new topic when need :slight_smile: