Hi,
Regarding the errors with the code:
appendField(new Blockly.FieldDropdown(this.generateOptions), 'SECOND_DROPDOWN')
needs to have a . before it so it's called on the input it's being appended to.
const otherVal = this.getSourceBlock().getFieldValue('FIRST_DROPDOWN');
would work every time generateOptions is called except the first before the field is mounted on the block. In this case this.getSourceBlock() will be null so trying to get the getFieldValue property causes the program to crash. You can easily correct this by making sure this.getSourceBlock() is not null and giving it a default option value otherwise as follows:
const otherVal = this.getSourceBlock() && this.getSourceBlock().getFieldValue('FIRST_DROPDOWN') || 'BOT';
Next, your generateOptions function needs to return an Array of option pairs. For each pair, the first specifies what the user will see for the text of the option whereas the second will specify the value you get from getFieldValue. So, for example, instead of return 'Window' for a 'BOT' selected value, you would use something like this:
if (otherVal === 'BOT') {
return [["Window", "WINDOW"], ["Door", "DOOR"], ["Wall", "WALL"]];
}
So incorporating these changes, your code would look like this:
Blockly.Blocks['ontology_block'] = {
init: function() {
this.appendDummyInput()
.appendField("Ontology")
.appendField(new Blockly.FieldDropdown([["BOT","BOT"], ["BEO","BEO"], ["SCHEMA","SCHEMA"]], this.validate), "FIRST_DROPDOWN");
this.appendDummyInput()
.appendField("Element")
.appendField(new Blockly.FieldDropdown(this.generateOptions), 'SECOND_DROPDOWN')
this.setInputsInline(false);
this.setNextStatement(true, null);
this.setColour(180);
this.setTooltip("");
this.setHelpUrl("");
},
generateOptions: function() {
const otherVal = this.getSourceBlock() && this.getSourceBlock().getFieldValue('FIRST_DROPDOWN') || 'BOT';
if (otherVal === 'BOT') {
return [["Window", "WINDOW"], ["Door", "DOOR"], ["Wall", "WALL"]];
}
if (otherVal=='BEO') {
return [["Wall", "WALL"], ["Something", "SOMETHING"], ["foo", "FOO"]];
}
},
validate: function() {
var secondDropdown = this.getSourceBlock().getField('SECOND_DROPDOWN');
var opts = secondDropdown.getOptions(false);
secondDropdown.setValue(opts[0][1]);
}
};
This will get it to work (for BOT and BEO, you'll need to add the other options), but when you switch from say BOT to BEO, you'll notice that it gives you BOT's first option of Window. This is because the first dropdown's value won't be set to the value of the newly selected option until *after* validation. What this means is that when you switch from BOT to BEO, the validate function will call secondDropdown.getOptions(false), which will in turn call generateOptions as you'd want it to. However, this.getSourceBlock().getFieldValue('FIRST_DROPDOWN') will still be 'BOT' so you still get the options of Window, Door, and Wall. Then the secondDropdown will have its value set to Window because of (secondDropdown.setValue(opts[0][1])). However, if you click the dropdown, you'll get the correct options because after validation, otherVal will correctly be BEO.
So, there are different approaches you can use here. As shown in fu6's code, you can remove the second dropdown and add one with a new set of options every time the first dropdown's selected option changes. Another possibility is to attach a property to your block that you use for the menu generator and that's changed in your validate function as follows:
Blockly.Blocks['ontology_block'] = {
init: function() {
this.currentValue = 'BOT';
this.appendDummyInput()
.appendField("Ontology")
.appendField(new Blockly.FieldDropdown([["Bot","BOT"], ["Beo","BEO"], ["Schema","SCHEMA"]], this.validate.bind(this)), "FIRST_DROPDOWN");
this.appendDummyInput()
.appendField("Element")
.appendField(new Blockly.FieldDropdown(this.generateOptions.bind(this)), 'SECOND_DROPDOWN')
this.setInputsInline(false);
this.setNextStatement(true, null);
this.setColour(180);
this.setTooltip("");
this.setHelpUrl("");
},
generateOptions: function() {
// this now refers to the block when it's called on the field dropdown because this was bound in init to the block
const otherVal = this.currentValue;
if (otherVal === 'BOT') {
return [["Window", "WINDOW"], ["Door", "DOOR"], ["Wall", "WALL"]];
}
if (otherVal=='BEO') {
return [["Wall", "WALL"], ["Something", "SOMETHING"], ["foo", "FOO"]];
}
if (otherVal=='SCHEMA') {
return [["Table", "TABLE"], ["Something else", "SOMETHING ELSE"], ["bar", "BAR"]];
}
},
validate: function(newValue) {
this.currentValue = newValue;
var secondDropdown = this.getField('SECOND_DROPDOWN');
var opts = secondDropdown.getOptions(false); // This regenerates the options for the dropdown
secondDropdown.setValue(opts[0][1]);
}
};
Best,
Jason