1
votes

I am trying to create a Handsontable in Handsontable (HOT-in-HOT) using Handsontable version 0.34.4CE/1.14.2 PRO. Everything works normally with the documentation available here... Handsontable but I want to create all of it dynamically using multiple multidimensional arrays.

The issue is that when you create a Handsontable normally you can assign all the variables just fine, and when you do it dynamically it works as well. When you introduce custom functions in the Handsontable then creating them dynamically is not as simple as it is normally.

As you can see in the code below, I realized I needed to pass the getValue() function as an expression for it to work. The problem is that the expression is created dynamically, therefore the variables in the function are not finalized in the scope of that function locally and are trying to be accessed at the time the function runs. I need the variables to save/set/assign to the variables in the function and try to get called after the expression is created.

Normal HOT-in-HOT from documentation...

  var
    carData = getCarData(),
    container = document.getElementById('example1'),
    manufacturerData,
    colors,
    color,
    colorData = [],
    hot;

  manufacturerData = [
    {name: 'BMW', country: 'Germany', owner: 'Bayerische Motoren Werke AG'},
    {name: 'Chrysler', country: 'USA', owner: 'Chrysler Group LLC'},
    {name: 'Nissan', country: 'Japan', owner: 'Nissan Motor Company Ltd'},
    {name: 'Suzuki', country: 'Japan', owner: 'Suzuki Motor Corporation'},
    {name: 'Toyota', country: 'Japan', owner: 'Toyota Motor Corporation'},
    {name: 'Volvo', country: 'Sweden', owner: 'Zhejiang Geely Holding Group'}
  ];
  colors = ['yellow', 'red', 'orange', 'green', 'blue', 'gray', 'black', 'white'];

  while (color = colors.shift()) {
    colorData.push([
      [color]
    ]);
  }

  hot = new Handsontable(container, {
    data: carData,
    colHeaders: ['Car', 'Year', 'Chassis color', 'Bumper color'],
    columns: [
      {
    type: 'handsontable',
    handsontable: {
      colHeaders: ['Marque', 'Country', 'Parent company'],
      autoColumnSize: true,
      data: manufacturerData,
      getValue: function() {
        var selection = this.getSelected();

        // Get always manufacture name of clicked row
        return this.getSourceDataAtRow(selection[0]).name;
      },
    }
      },
      {type: 'numeric'},
      {
    type: 'handsontable',
    handsontable: {
      colHeaders: false,
      data: colorData
    }
      },
      {
    type: 'handsontable',
    handsontable: {
      colHeaders: false,
      data: colorData
    }
      }
    ]
  });

The HOT-in-HOT setup I am trying to do dynamically...

if(data_arr[0][key][2]['cell_type'] == "handsontable" && data_table_1_col_headers_options_arr[key][0] != "NA")
{
    data_table_1_columns_arr[count]['handsontable'] = new Array();

    data_table_1_columns_arr[count]['handsontable']['colHeaders'] = data_arr[3][key][1][0];
    data_table_1_columns_arr[count]['handsontable']['autoColumnSize'] = true;
    data_table_1_columns_arr[count]['handsontable']['data'] = data_arr[3][key][0];

    //// THE ISSUE IS IN THE EXPRESSION BELOW. ////
    var temp_field_value_to_use = data_arr[3][key][1][1];
    var hot_in_hot_function = function ()
                {
                    var selection = this.getSelected();
                    var field_use = temp_field_value_to_use;
                    return this.getSourceDataAtRow(selection[0])[field_use];
                };

    data_table_1_columns_arr[count]['handsontable']['getValue'] = hot_in_hot_function;
}

As you can see in the dynamic version above the Handsontable is defined by multiple multidimensional arrays, of which only the relevant code is shown for this issue. There is a lot more code elsewhere that is used to configure the rest of the table. This specific section starts with a condition for the cell type. If the cell type id Handsontable then create the cell options for a HOT-in-HOT. Please note that this dynamic creation builds a parent Handsontable that has multiple columns that use different HOT-in-HOT. The issue is in the expression version of the code below the comment. The variable 'temp_field_value_to_use' is the index of the column in the HOT-in-HOT that I want to use for the value in the parent Handsontable. Since this value/column index changes based on that column in the parent Handsontable that has the HOT-in-HOT, the variable must save to the expression dynamically. Now when the code is all run, the variable 'temp_field_value_to_use' always gives the last assigned value, meaning it was not dynamically saved with the expression and is using the same function/expression for every HOT-in-HOT.

1

1 Answers

0
votes

I figured that since the expression was dynamically created the issue lies in how the expression was created and how its scope was setup. After a good bit of research, I found a solution. The solution uses what is called a JavaScript Closure, which is a self-invoking function. Please add to, or make better if you can, I hope this helps someone along the way. I also asked Handsontable to add too their documentation.

You can see in the code below that the outer function is assigned the dynamic variable, and thus the scope changes so the inner function uses the otter variable instead of the variable in the scope of the dynamic Handsontable options configuration loop.

if(data_arr[0][key][2]['cell_type'] == "handsontable" && data_table_1_col_headers_options_arr[key][0] != "NA")
{
    data_table_1_columns_arr[count]['handsontable'] = new Array();

    data_table_1_columns_arr[count]['handsontable']['colHeaders'] = data_arr[3][key][1][0];
    data_table_1_columns_arr[count]['handsontable']['autoColumnSize'] = true;
    data_table_1_columns_arr[count]['handsontable']['data'] = data_arr[3][key][0];

    var temp_field_value_to_use = data_arr[3][key][1][1];
    //// JavaScript Closure expression below. ////
    var hot_in_hot_function = (function ()
    {
        var field_use = temp_field_value_to_use;
        return function ()
        {
            var selection = this.getSelected();
            return this.getSourceDataAtRow(selection[0])[field_use];
        }
    })();

    data_table_1_columns_arr[count]['handsontable']['getValue'] = hot_in_hot_function;
}