Need search capability on Blocks having dropdown

232 views
Skip to first unread message

Shaleen Pandiya

unread,
Nov 23, 2021, 10:06:13 AM11/23/21
to Blockly
Hi Team

We are using Google blocks in our application. I have a requirement where i need to provide a search capability in the dropdown inside a block. See in the below example block as you see we have populated the dropdown data with our custom data source and on top of it we need a searchable text box above those values where user can search a particular value from the dropdown values.
Please suggest how we can achieve this.


Image.png

Beka Westberg

unread,
Nov 23, 2021, 2:47:22 PM11/23/21
to blo...@googlegroups.com
Hello,

To add searchability to a drop down, you're probably going to want to implement a custom field :/ But there is lots of (imo) good documentation about how to do so, including a full example of one. You might want to look into using the brower's combobox input as a starting point.

There are also projects who use a text input field in combination with a dynamic dropdown to create the searching effect. This looks a bit different than what it seems like you're looking for. But I think it works really well!

If you have any further questions once you get started, feel free to ask! We're happy to help =)
--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/6371b1a5-7647-4612-ac84-6b4b16f18956n%40googlegroups.com.

Sean

unread,
Nov 23, 2021, 4:28:19 PM11/23/21
to Blockly
I had to do something similar for a project I am working on. As Beka suggested, you'll want to make a custom field. The turtle example was a bit too complex for me when I started out. Instead I  used the Field Pitch example and modified it to what I wanted to achieve.  It's very simple once you figure out how it works. 
For my example I wanted the user to select from a list of predefined keyboard keys.  

Capture.PNG

Shaleen Pandiya

unread,
Nov 24, 2021, 1:07:37 AM11/24/21
to Blockly
Thank you guys for the prompt response. I am going through the suggested examples and will let you know my findings for the same in this thread.

Shaleen Pandiya

unread,
Nov 26, 2021, 4:42:10 AM11/26/21
to Blockly
Hi Sean

I was trying to modify the Field Pitch example to suit the need but was getting stuck at different places. I saw your snapshot and this exactly what i also want in my project (only data source will change). Would you mind sharing the code with us so that i can have a look and modify it to suit my need.

fu6...@gmail.com

unread,
Nov 27, 2021, 9:00:33 AM11/27/21
to Blockly
FieldFilter.mp4

Shaleen Pandiya

unread,
Nov 30, 2021, 8:06:58 AM11/30/21
to Blockly
Hi Fu6
First of all thanks for sharing the code. Post integration of your code with my application I am getting below error . Is this some JS files i am missing here. 

2021-11-30 18_35_40-Clipboard.png

fu6...@gmail.com

unread,
Nov 30, 2021, 11:10:47 AM11/30/21
to Blockly
Please remove the line from 14 to 16.

Shaleen Pandiya

unread,
Dec 1, 2021, 5:34:01 AM12/1/21
to Blockly
Thanks it worked for me. 

fu6...@gmail.com

unread,
Dec 1, 2021, 11:42:31 PM12/1/21
to Blockly
Hi,
I found a bug in my sample code and fixed it. 
Please update the code. 
If you find any bug please reply!

Blockly.Blocks["fieldFilter"] = { init: function() {
var options = ['','aaa','abc','add','bbb','bcd','ccc','def','deg']; 
  this.appendDummyInput()          .appendField(new CustomFields.FieldFilter('', options), 'FILTER');
  this.setStyle('loop_blocks');   this.setInputsInline(true);   this.setPreviousStatement(true, null);   this.setNextStatement(true, null); } };
fieldFilter.mp4
Message has been deleted

Shaleen Pandiya

unread,
Dec 7, 2021, 7:13:25 AM12/7/21
to Blockly
Thanks for the update. Actually I had updated the code to pass the data source from my calling function. Though i have other problem with the datasource which we have. Actually its an array of an array like this (like an Key value pair). 
DataSource.png
There it starts breaking in the code since it was expecting may be a array only.

PFA my code JS file which i used. Please do rename SearchableDropdown.txt file to SearchableDropdown.js for use (done since Forum restricts attaching JS files)
Also the calling part is also listed below.

var dataSource = blocklyNamespace.Helper.AddDefaultToDropdown(blocklyNamespace.Constants.RESOLVER,blocklyNamespace.Data.Resolvers);

 this.appendDummyInput('ipResolver')
            .appendField('Resolve Value Of')
            .appendField(new CustomFields.FieldFilter(
                "",dataSource),'ddlResolver');
        this.setOutput(true, null);
        this.setColour(blocklyNamespace.Constants.Colours.BLUE);
        this.setTooltip("");
        this.setHelpUrl("");
        this.setStyle('loop_blocks');
        this.setInputsInline(true);
        //this.setPreviousStatement(true, null);
        //this.setNextStatement(true, null);

If you notice i have commented last 2 line since it was giving error as highlighted below.
Error.png

Please do help in resolving the issues. 
SearchableDropdown.txt

Beka Westberg

unread,
Dec 7, 2021, 10:22:54 AM12/7/21
to blo...@googlegroups.com
Hello,

Blockly currently has a restriction where your block cannot have an output and a previous connection at the same time :/ There is great work being done to remove this restriction, but for now you will have to make sure to only have one or the other at any time.

So to fix your block you should just need to remove the setOutput line:
```
 this.appendDummyInput('ipResolver')
            .appendField('Resolve Value Of')
            .appendField(new CustomFields.FieldFilter(
                "",dataSource),'ddlResolver');
        // Remove the below line.
        // this.setOutput(true, null);

        this.setColour(blocklyNamespace.Constants.Colours.BLUE);
        this.setTooltip("");
        this.setHelpUrl("");
        this.setStyle('loop_blocks');
        this.setInputsInline(true);
        // Then you can uncomment the setPreviousStatement() call.
        // this.setPreviousStatement(true, null);
        this.setNextStatement(true, null);
```

I hope that helps! If you have any further questions please reply!
--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.

Shaleen Pandiya

unread,
Dec 8, 2021, 12:53:29 AM12/8/21
to Blockly
Thanks Beka that resolved my one issue. Really appreciate it.
My other issue about the data source being an array of an array, any help or comments there by anyone would be highly appreciated. 

Beka Westberg

unread,
Dec 8, 2021, 9:42:22 AM12/8/21
to blo...@googlegroups.com
Hmm I'm not sure if I'm fully parsing the issue, but I can try to give a suggestion anyway. The default dropdown expects an array of arrays when you're constructing it, so you should be fine in that case. However if you are using a custom dropdown field, you may need to loop over your pairs, and extract either the first element or the second element to create a one dimensional array.

I hope that helps! And if I didn't understand the question please feel free to correct me hehe.
--Beka

fu6...@gmail.com

unread,
Dec 12, 2021, 12:18:05 PM12/12/21
to Blockly
Hi, I wrote the sample code for you.

Blockly.Blocks["test_fieldDropdownFilter"] = {
  init: function() {
        this.options = [
                ['',''],        
                ['ap','apple'],
                ['ba','banana'],
                ['ch','cherry']
        ];
        var options = [];
        this.options.forEach(
                element => options.push(element[0])
        );
       
    this.field = new CustomFields.FieldFilter('', options, this.validate);
    this.appendDummyInput()
        .appendField('Filter')
        .appendField(this.field, 'FILTER');
               
    this.appendDummyInput()
        .appendField('', 'VALUE');

    this.setStyle('loop_blocks');
    this.setInputsInline(true);                
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);        
  },
  validate: function(newValue) {
        const block = this.sourceBlock_;
        block.options.forEach(function(element) {
                if (element[0]==CustomFields.FieldFilter.WORDS[Number(newValue)]) {
                        console.log(element[1]);
                        block.setFieldValue(element[1], 'VALUE');
                }
        })
  }
};

Blockly.Arduino['test_fieldDropdownFilter'] = function(block) {
  return this.getFieldValue('VALUE');
};


You need to update the function.

CustomFields.FieldFilter = function(text, options, opt_validate) {
  CustomFields.FieldFilter.superClass_.constructor.call(this, text, opt_validate);
 
  CustomFields.FieldFilter.INITWORDS = options;
  CustomFields.FieldFilter.WORDS = CustomFields.FieldFilter.INITWORDS;

  this.setSpellcheck(false);
  this.clickWrapper_ = null;
  this.moveWrapper_ = null;
  this.downWrapper_ = null;        
};
FieldFilter.mp4

fu6...@gmail.com

unread,
Dec 15, 2021, 8:01:49 AM12/15/21
to Blockly
Hi,
I modify the code and support user to set different dropdown lists. Please update it.
fieldfilter.mp4

Samuel Alvarez

unread,
Dec 19, 2021, 7:57:45 AM12/19/21
to Blockly
Hi, I am following these conversation cause I have the same problem as Shaleen. 
With the last updated worked for me, however I need the same search capability but without know what array of arrays I will have. 
I have a block that allows you to choose first a country and after a region inside that country. So I need a function that creates the "options" like the generic dropdown field. 
Also, I don't want to show the real value, just the translated country. The value has to go directly to the code. In the dropdownfield it was a default function.
Any ideas on how to do these? 
Thanks you very much

fu6...@gmail.com

unread,
Dec 19, 2021, 11:43:20 AM12/19/21
to Blockly
fieldfilter.mp4

fu6...@gmail.com

unread,
Dec 19, 2021, 7:42:03 PM12/19/21
to Blockly
I modified the lines to fix a bug.
https://github.com/fustyles/webduino/blob/f1689496874425a442e50eefb63f692d380002bf/LinkIt7697/test_myFieldFilter_2Level/javascript.js#L187


Blockly.Blocks["test_fieldFilter"] = {
  init: function() {
    this.options = [
            ['','','',''],        
            ['France','fr','Paris','CDG'],
            ['France','fr','Laon','XLN'],
            ['France','fr','Nice','NCE'],                
            ['Taiwan','tw','Taipei','TPE'],
            ['Taiwan','tw','Taichung','RMQ'],                
            ['Taiwan','tw','Kaohsiung','KH']
    ];
    var options1 = [];
    this.options.forEach(function(element) {
              if (!options1.includes(element[0]))
                      options1.push(element[0])
    });
       
    this.field1 = new CustomFields.FieldFilter('', options1, this.validate1);
    this.appendDummyInput()
          .appendField(this.field1, 'FILTER1');
    this.appendDummyInput()
          .appendField('', 'VALUE1');
    //this.getField("VALUE1").setVisible(false);                
               
    this.appendDummyInput('zone')
           .appendField('', 'FILTER2');        
    this.appendDummyInput()
           .appendField('', 'VALUE2');        
    //this.getField("VALUE2").setVisible(false);        


    this.setStyle('loop_blocks');
    this.setInputsInline(true);                
    this.setPreviousStatement(true, null);
    this.setNextStatement(true, null);        
  },
  validate1: function(newValue) {
        const block = this.sourceBlock_;
        if (newValue=="") {
                if (block.field1.WORDS) {
                        if (block.field1.WORDS.length>0) {
                                block.options.forEach(function(element1) {
                                        if (element1[0]==block.field1.WORDS[0]) {
                                                block.setFieldValue(element1[1], 'VALUE1');
                                                block.zoneOptions(element1);
                                        }
                                })                                
                        }
                }
                else {
                        block.setFieldValue('', 'VALUE1');
                        block.setFieldValue('', 'VALUE2');
                }
        }
        else {
                block.options.forEach(function(element1) {
                        if (element1[0]==block.field1.WORDS[Number(newValue)]) {
                                block.setFieldValue(element1[1], 'VALUE1');
                                block.zoneOptions(element1);
                        }
                })
        }
  },
  validate2: function(newValue) {
        const block = this.sourceBlock_;
        if (newValue=="") {
                if (block.field2.WORDS) {
                        if (block.field2.WORDS.length>0) {
                                block.options.forEach(function(element) {
                                        if (element[2]==block.field2.WORDS[0]) {
                                                block.setFieldValue(element[3], 'VALUE2');                                                
                                        }
                                })                                
                        }
                }
                else {
                        block.setFieldValue('', 'VALUE2');
                }
        }
        else {
                block.options.forEach(function(element) {
                        if (element[2]==block.field2.WORDS[Number(newValue)]) {
                                block.setFieldValue(element[3], 'VALUE2');
                        }
                })
        }
  },
  zoneOptions: function(element1) {
                var options2 = [];
                this.options.forEach(function(element2) {
                        if (element1[0]==element2[0])
                                options2.push(element2[2])
                })
                this.field2 = new CustomFields.FieldFilter('', options2, this.validate2);
                if (this.getField("FILTER2"))
                        this.getInput("zone").removeField("FILTER2");
                this.getInput("zone").appendField(this.field2, 'FILTER2');
                this.setFieldValue('', 'VALUE2');
  }
};

Blockly.Arduino['test_fieldFilter'] = function(block) {
  return this.getFieldValue('VALUE1')+","+this.getFieldValue('VALUE2');
};

Samuel Alvarez

unread,
Dec 20, 2021, 7:20:11 AM12/20/21
to Blockly
Thank you very much, I am trying to introduce all the changes into my code. The moment I figure out how to do it I'll tell you.
Thanks again
Reply all
Reply to author
Forward
0 new messages