Update inputDummy dropdown fields based on selection of another inputDummy dropdown field

1,009 views
Skip to first unread message

Utkarsh Mehta

unread,
Apr 24, 2021, 8:30:39 AM4/24/21
to Blockly

Hi everyone,

I've been trying to make a custom block in the Blockly workspace that changes the options in drop-down fields based on the selection of the previous drop-down field in the same block.

But I'm facing an issue while removing and adding a different dropdown with new options. You can see the complete issue with an elaborate explanation here at Stackoverflow.

Please assist

Thanks,
Utkarsh

Beka Westberg

unread,
Apr 24, 2021, 5:45:07 PM4/24/21
to blo...@googlegroups.com
Hello,

It seems like there might be a bit of confusion about inputs vs fields and how to deal with them. Inputs are like the different "rows" of your block. They can optionally include a place to put value blocks (puzzle piece input) or statement blocks (lego input). Fields can then be added to inputs. Fields are things like text boxes, dropdowns, images, etc. Each input can be given a name, and each field can be given a name. As such, it's best to give them different names to avoid confusion about what your code is referring to =)

As for removing a field from an input, I believe you are going to want to use `block.getInput('INPUT_NAME').removeField('FIELD_NAME')`. However, I'm not sure why that was throwing errors saying removeField is not a function... If that occurs again I suggest logging the input object to the console to verify it's actually an input.

I hope that gives you some places to start! Best of luck on your project!
--Beka

--
You received this message because you are subscribed to the Google Groups "Blockly" group.
To unsubscribe from this group and stop receiving emails from it, send an email to blockly+u...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/blockly/1d2ec9bb-b7f0-4705-b8a0-7d2e0d6a8137n%40googlegroups.com.

Utkarsh Mehta

unread,
Apr 25, 2021, 2:14:00 AM4/25/21
to Blockly
Hi Beka,

Thanks so much for the reply and that great explanation of the Blockly concept.
My block now responds as desired.

Thanks again,
Utkarsh

Utkarsh Mehta

unread,
May 1, 2021, 5:38:27 AM5/1/21
to Blockly
Hi,

Just a follow-up question. How can I get the selected value of the drop-down from the XML responsible for creating the blockly workspace.
When I try to "this.getFieldValue('fieldName')", on "events.FINISHED_LOADING", it returns the 1st option of the drop-down field, even when on the blockly workspace UI, it shows for e.g. option3.

Thanks,
Utkarsh

Beka Westberg

unread,
May 1, 2021, 3:19:42 PM5/1/21
to blo...@googlegroups.com
Hello Utkarsh,

That's definitely some weird behavior! Could you send a list of steps to reproduce it? When I tried it in the playground with the logic_operation block it seemed to work:
```
  workspace.addChangeListener((e) => {
    if (e.type == Blockly.Events.FINISHED_LOADING) {
      var blocks = workspace.getBlocksByType('logic_operation');
      console.log(blocks[0].getFieldValue('OP'));  // Correctly logged the value.
    }
  });
```

Best,
Beka

Utkarsh Mehta

unread,
May 2, 2021, 2:01:50 AM5/2/21
to Blockly
Hi Beka,

I'm working on the same block as before. Three drop-down fields each dependant on the other. The names of these drop-downs are table, column, column1.
  1. Table drop-down is to list all the tables in the localStorage.
  2. The Column drop-down is to list all the columns of the selected table.
  3. Column1 drop-down is to list the columns of the selected table, excluding the one selected on the "column" drop-down.
I applied the logic that you've mentioned in your last mail like this, inside the onchange function of block initialization.

if (event.type === Blockly.Events.FINISHED_LOADING) {
            // workspace finished loading, update column_input_field and column_input1_field based on selected table and column
            var workspace = Blockly.Workspace.getById(this.workspace.id)
            var block = workspace.getBlockById(this.id)
            console.log('tableinput'block.getFieldValue('table_input_field'))
            console.log('columninput'block.getFieldValue('column_input_field'))
}

Output (Console logs):
  1. attached warning
  2. tableinput <id for t3>
  3. columninput <id for t1c1>

Outcome:
  1. When I use the block for the first time, i.e. when the logic for FINISHED_LOADING doesn't matter, I am able to use the block as designed.
  2. Suppose, I select table option t3, my column and column1 drop-down are populated accordingly. And in the generator code, I get the desired values for table, column and column1.
  3. But when I reload my workspace
    1. the table drop-down is selected correct, t3
    2. but the column fields lose their value and revert to default values, i.e. columns of table t1 with the warning seen in the attached snippet.
    3. As seen in the error, the workspace is trying to select the correct value for column, but because the drop-down is getting populated again on FINISHED_LOADING it is not able to.
Desired outcome:
  1. Save block state
  2. Populate and select the correct values for the drop-down values of the block on workspace reload.
What I am attempting:
As a work-around, what I plan on doing is loading all the drop-downs the way it is happening now. And at the end, just manually select a value somehow.
P.S.: I did come through a function/method to select a value for a dropdown using code. Not able to recall it at the moment.

Thanks,
Utkarsh
blockWorkspaceColumnDefaultValues.PNG

Beka Westberg

unread,
May 3, 2021, 7:32:23 PM5/3/21
to blo...@googlegroups.com
Hello,

Firstly to clarify, you're just using the finished loading listener for debugging? Or is there something you're trying to do with it?

Now with regard to the warning you're receiving, in general, this is caused by your menu generator for your dropdown not functioning correctly during serialization. You need to make sure that your dropdown blocks behave correctly even when the block is only partially deserialized. I wonder if the fact your selectedColumn is only being grabbed once, instead of everytime the menu generator function is called, might be a problem. I'd like to give more specific advice, but I'm not knowledgeable enough about your setup/situation :/

Also, that value you've underlined in your warning image is surprising. Is that a value you expect to be held in your dropdown? If not can you check that it actually exists in the serialized XML? I think it has to be for the code to be getting to the point where this error is triggered... but I just want to make sure hehe.

I know this answer probably isn't very helpful :/ But I hope it gives you some places to start!
--Beka

Jason Schanker

unread,
May 4, 2021, 2:07:34 AM5/4/21
to Blockly
Hi Utkarsh and Beka,

I made some modifications to the code so that the options for both column field dropdown menus are generated from the selected table (instead of the 0th one) upon block initialization.  However, the problem of unavailable options still persists during deserialization if the selected table wasn't the first one.  As far as I can tell, this is because first, the selected option always starts out as the first one from the dropdown menu when deserializing.  This means that the column dropdowns will initially be populated with columns from the first table.  And then second, once the table id is set to some other value, the column options will not be changed.  This then results in the error when trying to set the column dropdown menu values to column ids from some other table.

We can work around this by using a mutation to store the selected table id when serializing.  Then during deserialization, we set the table field dropdown's value to this id, and then call getOptions on the two dropdown column fields to regenerate the two dropdowns to have the column ids from this selected table.  The column fields will then be able to be set to their appropriate values.  However, while this works, this was a little unexpected to me as I would've thought that getOptions wouldn't be changing state (so long as the menu generator it was passed didn't do so).

By generating the options for the column dropdowns from the currently selected table, removing/adding fields upon changes became unnecessary.  However, since the other dropdowns didn't change their selected options when a given menu option was selected, we still needed to have a change listener that would change the column fields to valid ones in response.  Below is the updated code.  Please let me know if you want any further clarification.

Best,
Jason

// the block JSON declaration variable
const updateTableData = {
    "type": "al_update_table_data",
    "message0": "Update in table %1 where column %2 is %3 set value for column %4 to %5",
    "args0": [
        {
            "type": "input_dummy",
            "name": "table_input",
        },
        {
            "type": "input_dummy",
            "name": "column_input",
        },
        {
            "type": "input_value",
            "name": "get_value"
        },
        {
            "type": "input_dummy",
            "name": "column_input1",
        },
        {
            "type": "input_value",
            "name": "set_value"
        }
    ],
    "inputsInline": false,
    "previousStatement": null,
    "nextStatement": null,
    "fieldRow": false,
    "colour": 90,
    "mutator": "field_updater", // added mutator
    "tooltip": "Update value in a table",
    "helpUrl": "",
    "extensions": ["get_tables", "get_column", "get_column1"],
}

// the blockly extensions
// get list of tables and populate the 'table_input' drop-down field
Blockly.Extensions.register('get_tables', function () {
  this.getInput("table_input")
    .appendField(new Blockly.FieldDropdown(
      // removed options declarations
        () => JSON.parse(localStorage.getItem('applab_myTables')) // tables
                .map(t => [t.name, t.id]) // options
    ), "table_input_field")
})

// get list of columns from the first table and populate the 'column_input' drop-down field
Blockly.Extensions.register('get_column', function () {
  this.getInput('column_input')
    .appendField(new Blockly.FieldDropdown(
        () => { // used arrow function so that this refers to the block, used selected table id to generate columns 
          const table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === this.getFieldValue("table_input_field"))
          return Object.keys(table['columnData'])
            .filter(cId => table['columnOrder'].includes(cId))
            .map(cId => [table['columnData'][cId]['name'], cId])
        }
    ), 'column_input_field')
})

// get list of columns from the first table, remove the column value already selected in 'column_input' and populate the 'column_input1' drop-down field
Blockly.Extensions.register('get_column1', function () {
  this.getInput('column_input1')
    .appendField(new Blockly.FieldDropdown(
        () => {
          const selectedColumn = this.getFieldValue('column_input_field');
          const table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === this.getFieldValue("table_input_field"))
          return Object.keys(table['columnData'])
            // combined filters into one
            .filter(cId => table['columnOrder'].includes(cId) && cId !== selectedColumn)
            .map(cId => [table['columnData'][cId]['name'], cId])
        }
    ), 'column_input1_field')
})

// mutator mixin
fieldToggleMixin = {
  mutationToDom: function() {
    // store table id in mutator when serializing
    const container = Blockly.utils.xml.createElement('mutation');
    container.setAttribute('tableId', this.getFieldValue("table_input_field"));
    return container;
  },
  domToMutation: function(xmlElement) {
    // set field value of selected table upon serializing and call getOptions on column input fields to generate columns from corresponding table  
    this.setFieldValue(xmlElement.getAttribute("tableId"), "table_input_field")
    this.getField("column_input_field").getOptions();
    this.getField("column_input1_field").getOptions();
  }
};

Blockly.Extensions.registerMixin('field_updater',
  fieldToggleMixin,
  () => "NOOP");

Blockly.Blocks['al_update_table_data'] = {
  init: function () {
    this.jsonInit(updateTableData)
  },
  onchange: function(event) {
    // change selected column dropdown menu options to first possible one if they're no longer choices  
    const fieldNames = ["column_input_field", "column_input1_field"];
    fieldNames.forEach(fieldName => {
      const fieldOptions = this.getField(fieldName).getOptions();
      if(!fieldOptions.map(opt => opt[1]).includes(this.getFieldValue(fieldName))) {
        this.setFieldValue(fieldOptions[0][1], fieldName)
      }
    })

Utkarsh Mehta

unread,
May 7, 2021, 2:18:03 AM5/7/21
to Blockly
Hi Jason & Beka,

Thank you for your insightful replies.

Beka,
I was using the FINISHED_LOADING event to update the drop-down options based on the selected values of other dropdowns. But it seems, FINISHED_LOADING wasn't helpful with getting field values. Also, the ID marked in the screenshot is as expected. Using MongoDB to generated IDs, so that's that.

Jason,
I applied the mutator to my block as you mentioned.
XML.getAttribute() worked just as expected and I was able to log my pre-selected IDs.
Although, your complete code didn't work, so I made changes, as listed below.
Also, I did not completely understand your onchange function in Blockly.Block initialization. Especially, the getOptions() part. You seem to have used this function in the mutator as well. Upon reading, I saw getOptions() only returns the 2D array of this.dropdown. But how does using getOptions() in the mutator make any difference?

Anyways, do assess my code and let me know if there could be an improvement. I myself can see repetitive code, just not aware of how to refactor it.

Thanks so much,
Utkarsh

// mutator register
Blockly.Extensions.registerMixin('field_updater',
    {
        mutationToDom: function () {
            // store table id in mutator when serializing
            const container = Blockly.utils.xml.createElement('mutation');
            container.setAttribute('tableId'this.getFieldValue("table_input_field"));
            container.setAttribute('columnId'this.getFieldValue('column_input_field'));
            container.setAttribute('column1Id'this.getFieldValue('column_input1_field'));
            return container;
        },
        domToMutation: function (xmlElement) {
            // set field value of selected table upon serializing and call getOptions on column input fields to generate columns from corresponding table
            // update table drop down
            this.setFieldValue(xmlElement.getAttribute("tableId"), "table_input_field")
            // update column drop down
            this.getInput('column_input').removeField('column_input_field')
            this.getInput('column_input')
                .appendField(new Blockly.FieldDropdown(
                    function () {
                        let options = []
                        let table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === xmlElement.getAttribute('tableId'))
                        Object.keys(table['columnData']).filter(cId => table['columnOrder'].includes(cId)).map(cId => options.push([table['columnData'][cId]['name'], cId]))
                        return options
                    }
                ), 'column_input_field')
            // set value for column drop down
            this.setFieldValue(xmlElement.getAttribute("columnId"), 'column_input_field')
            // update column1 drop down
            this.getInput('column_input1').removeField('column_input1_field')
            this.getInput('column_input1')
                .appendField(new Blockly.FieldDropdown(
                    function () {
                        let options = []
                        let table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === xmlElement.getAttribute('tableId'))
                        Object.keys(table['columnData']).filter(cId => table['columnOrder'].includes(cId) && cId !== xmlElement.getAttribute('columnId')).map(cId => options.push([table['columnData'][cId]['name'], cId]))
                        return options
                    }
                ), 'column_input1_field')
            // set value for column1 drop down
            this.setFieldValue(xmlElement.getAttribute('column1Id'), "column_input1_field")
        }
    });



// block initialization
Blockly.Blocks['al_update_table_data'] = {
    init: function () {
        this.jsonInit(updateTableData)
    },
    onchange: function (event) {
        if (event.blockId === this.id) {
            // something happened on this block
            if (event.type === Blockly.Events.BLOCK_CHANGE) {
                // change detected
                console.log('name of changed field'event.name)
                console.log('old value'event.oldValue)
                console.log('new value'event.newValue)
                if (event.name === 'table_input_field') {
                    // change in table, update column_input and column_input1
                    this.getInput('column_input').removeField('column_input_field')
                    this.getInput('column_input')
                        .appendField(new Blockly.FieldDropdown(
                            function () {
                                let options = []
                                let table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === event.newValue)
                                Object.keys(table['columnData']).filter(cId => table['columnOrder'].includes(cId)).map(cId => options.push([table['columnData'][cId]['name'], cId]))
                                return options
                            }
                        ), 'column_input_field')
                    var selectedColumn = this.getFieldValue('column_input_field')
                    this.getInput('column_input1').removeField('column_input1_field')
                    this.getInput('column_input1')
                        .appendField(new Blockly.FieldDropdown(
                            function () {
                                let options = []
                                let table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === event.newValue)
                                Object.keys(table['columnData']).filter(cId => table['columnOrder'].includes(cId)).filter(cId => cId !== selectedColumn).map(cId => options.push([table['columnData'][cId]['name'], cId]))
                                return options
                            }
                        ), 'column_input1_field')
                } else if (event.name === 'column_input_field') {
                    // change in selected column, update column_input1
                    var table_id = this.getFieldValue('table_input_field')
                    this.getInput('column_input1').removeField('column_input1_field')
                    this.getInput('column_input1')
                        .appendField(new Blockly.FieldDropdown(
                            function () {
                                let options = []
                                let table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === table_id)
                                Object.keys(table['columnData']).filter(cId => table['columnOrder'].includes(cId)).filter(cId => cId !== event.newValue).map(cId => options.push([table['columnData'][cId]['name'], cId]))
                                return options
                            }
                        ), 'column_input1_field')
                }
            }
        }
    }
}

Jason Schanker

unread,
May 7, 2021, 11:22:17 PM5/7/21
to Blockly
Hi Utkarsh,

Could you tell me what error you're getting?  I had tested the code locally before posting, and it worked.  I just forked the code sandbox from https://groups.google.com/g/blockly/c/X4ZyWJF7tZU, adding (1) the code I posted, (2) code to set localStorage with appropriate test data, and (3) code to load two of these blocks from XML after about 5 seconds.  (I also added a const before fieldToggleMixin to declare the variable so you don't get an error if running in strict mode as well as an empty JavaScript language generator for the block.)

The code is available here: https://codesandbox.io/s/fervent-shannon-tzdfx?file=/src/index.js (You will need to reload each time you make changes because otherwise you'll get an error about "get_tables" already being registered.)
You can test it without the code editor here: https://tzdfx.csb.app/

To address your questions about the changes I made and make some suggestions, I'll lead off by explaining how the options for a dropdown menu are determined: 

When you pass a reference to a function as the first argument to the Blockly.FieldDropdown constructor as you do here, this function will become the menu generator that getOptions calls to determine the list of options.  getOptions is called when the dropdown is being created, when the user clicks on that dropdown, and when you call it directly.  Each time getOptions is called, the menu generator function you supplied is called to get its return value, which is then stored and used as the (possibly new) list of options.  When the dropdown is being created, it is initialized having the first option selected (https://github.com/google/blockly/blob/master/core/field_dropdown.js#L84).

So now let's see what goes wrong and why calling getOptions on the two column fields after setting the table field fixes this.  First upon deserialization from XML:

Say the first referenced table in the dropdown has columns A, B, C, and D and the second table has columns E, F, and G.  When the table field is created, it does so with the first option selected by default.  So when the menu generator passed to the column_input_field dropdown constructor is first called, it returns the columns from this first table, mainly A, B, C, and D with A selected (the first option).  Then when the menu generator for column_input_field1 is first called, it will return the list of columns from the first table excluding the selected A of the second dropdown, mainly the list of B, C, and D.  

But suppose the XML specified that the selected values for table_input_field, column_input_field, and column_input1_field should be the second table, G, and E, respectively, instead.  The second table is a valid option so there's no problem setting the table.  But after doing that, it does not regenerate the options for the column input fields.  So when it tries to set column_input_field to G, it says that's not possible because my only available options are A, B, C, and D.  Similarly, when trying to set column_input_field1 to E, it fails saying my only options are B, C, and D.

However, you can fix this by writing code in domToMutation, which will be called before the computer sets the values of fields as specified in the XML.  So we call this.setFieldValue to set the table_input_field to the tableId specified by the mutation.  This by itself would be unnecessary as the table dropdown would automatically be set to the correct value from the XML.  But by then calling getOptions on column_input_field and column_input_field after setting the table, you are forcing their corresponding menu generators to be called again when the selected option is the second table.  This means that the menu generator for column_input_field will instead return the list of options of columns from the second table, mainly E, F, and G.  The options for column_input1_field will also be E, F, and G.  So now the field values specified in the XML can be successfully set.  As before, table is set to the second one, which does nothing since it was already set in domToMutation.  But this time the selected options of G for column_input_field and E for column_input1_field are both valid so they go through.

Now to address the change listener.  When reading that column_input1_field will also be E, F, and G, you may have wondered why column E was not excluded.  That's because the original selected option of A was not changed by calling getOptions, even if it's no longer a valid option.  So when the values for column_input1_field are generated, it only checks that E, F, and G are not A, which they of course are not.  

Similarly, using this example from before, if you click the table dropdown and change the selected table from table 2 back to table 1, the selected option for column_input_field remains G.  This is true even though the menu generator now returns the list of columns from the selected first table of A, B, C, and D.  To fix this issue, I had the onchange listener first call getOptions for column_input_field to see if the selected option was present.  If not, it would set it to the first available one.  So in this case fieldOptions would be set to [["Column A", "a"], ["Column B", "b"], ["Column C", "c"], ["Column D", "d"]], the return value from getOptions called on the field column_input.  Calling map(opt => opt[1]) on this results in ["a", "b", "c", "d"], which it then checks to see if it includes "g".  Since it doesn't, it selects fieldOptions[0][1], the first option value of "a" and sets it as the new field value for column_input_field.  Next, it calls getOptions for column_input1_field, which now results in ["Column B", "b"], ["Column C", "c"], ["Column D", "d"]].  Since e is not in this list, it sets it to the first available one of b.

Hopefully, this long explanation of getOptions (https://github.com/google/blockly/blob/master/core/field_dropdown.js#L479) and what I was doing made sense!

So finally, to address changes I made and how you could shorten your code:

1. As alluded to, you don't need to call the variable options in the menu generator function you use as the menu is only determined by the return value and is unaffected by the choice of local variable names.  So for the get_tables extension, for example, you can easily use a one-line arrow function definition (without braces or the use of the return keyword):

      () =>
        JSON.parse(localStorage.getItem("applab_myTables")) // tables
          .map((t) => [t.name, t.id]) // options

The same applies to the other two extensions, which I had modified accordingly.  

Also as a few stylistic notes if you were to stick with what you had, you could use const options and const table instead of let options and let table.  For table, the use of const may seem a little nonintuitive as it initially refers to an empty array before adding values to it, so it doesn't seem to be constant.  However, in JavaScript, const just declares that you're not changing the reference to the object that the variable stores; the object's (Array's) properties can still be changed.  E.g., you always refer to the same car even though the car's miles traveled, fuel, etc. can change.  In general, it's better to declare variables with const when you don't expect their values to change as a way of letting readers know this and as a way of preventing accidental modifications.  Also, I'd avoid using an instruction that changes state (push called on options) in map as map is generally only used to create a new Array from an existing one.  Since the function you're passing is used only for its side effects, mainly appending to options, and you're only using map to repeatedly call it for each table in the Array, I'd use forEach instead.

2. filter(cId => table['columnOrder'].includes(cId)).filter(cId => cId !== selectedColumn) could be replaced with filter(cId => table['columnOrder'].includes(cId) && cId !== selectedColumn) as I had changed.  This creates a new Array in which both conditions were simultaneously met instead of first creating a new subarray of the items meeting the first condition of table['columnOrder'].includes(cId) and then creating a new subarray from the resulting one in which the second condition cId !== selectedColumn was met.

3. Removing and adding fields is also unnecessary.  As mentioned earlier, you can call getOptions to regenerate them in response to changes in the menu options that are selected and use the onchange listener to simply change the selected options when they're no longer in the lists of valid ones.

4. If you went with your current implementation, you could remove the repetition of the anonymous functions by giving them a name and then passing a reference to it:

E.g.:

function columnInputFieldGenerator() {
  let options = []
  let table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === event.newValue)
  Object.keys(table['columnData']).filter(cId => table['columnOrder'].includes(cId)).map(cId => options.push([table['columnData'][cId]['name'], cId]))
  return options
}

In that case, you could replace the occurrences of:

this.getInput('column_input')
    .appendField(new Blockly.FieldDropdown(
        function () {
            let options = []
            let table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id === xmlElement.getAttribute('tableId'))
            Object.keys(table['columnData']).filter(cId => table['columnOrder'].includes(cId)).map(cId => options.push([table['columnData'][cId]['name'], cId]))
            return options
        }
    ), 'column_input_field')

with this.getInput('column_input')
         .appendField(new Blockly.FieldDropdown(columnInputFieldGenerator), 'column_input_field')

There is more refactoring that you could do to condense your code even more, but given that this response is already too long, I'll hold off on any further suggestions for now.

Hopefully this makes sense.  Feel free to respond with any additional questions.

Best,
Jason
Message has been deleted

Jason Schanker

unread,
May 8, 2021, 1:18:21 AM5/8/21
to Blockly
Quick correction to suggestion 4 as the two functions differ by what they check table.id against.  You could instead use:

function columnInputFieldGenerator(tableId) {
  let options = []
  let table = JSON.parse(localStorage.getItem('applab_myTables')).find(table => table.id ===  tableId)
  Object.keys(table['columnData']).filter(cId => table['columnOrder'].includes(cId)).map(cId => options.push([table['columnData'][cId]['name'], cId]))
  return options
}

And then use:

this.getInput('column_input')
         .appendField(new Blockly.FieldDropdown(columnInputFieldGenerator.bind(this, xmlElement.getAttribute('tableId'))), 'column_input_field')

and

this.getInput('column_input')
         .appendField(new Blockly.FieldDropdown(columnInputFieldGenerator.bind(this,  event.newValue)), 'column_input_field')

Alternatively instead of using columnInputFieldGenerator.bind(this,  ...) which will create a new 0-input function from the 1-input columnInputFieldGenerator where tableId will be bound or assigned to whatever the ... is, you could use the following:
() =>  columnInputFieldGenerator(xmlElement.getAttribute('tableId')) and () => columnInputFieldGenerator(event.newValue), respectively.

Reply all
Reply to author
Forward
0 new messages