Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

custom fields #8

Closed
dpasdar opened this issue Nov 12, 2019 · 8 comments
Closed

custom fields #8

dpasdar opened this issue Nov 12, 2019 · 8 comments

Comments

@dpasdar
Copy link

dpasdar commented Nov 12, 2019

Hello,
thanks for the great library. I appreciate if you can give me a hint on how I could create/integrate a custom field with/without this library somehow. The examples here use google closure, especially the inheritance doesn't seem to be straight forward without using it. The use-case for us is a typeahead suggestion type of text-input/dropdown, which is sadly not supported out of the box.

I appreciate your help/tips. I would be willing to code/fork/integrate our code into the library once I finished developing it.

@roroettg
Copy link
Owner

Hi,
thanks for using ngx-blockly. To be honest I have not used Customfields yet.
I would assume that it will work with ngx-blockly the same way as described in the docs.

Maybe the code examples are more helpful:

https://blockly-demo.appspot.com/static/demos/custom-fields/index.html
https://github.com/google/blockly/tree/master/demos/custom-fields

@dpasdar
Copy link
Author

dpasdar commented Nov 18, 2019

Thanks for your answer, I have tried the documentation, however it required google-closure library(in code examples goog(***)) somehow and it is not clear how one can integrate it in the global context with angular. I was hoping you have used or seen it somewhere during the development of this plugin and can shed some light on this.

@roroettg
Copy link
Owner

I think I managed to create a CustomField without google closure. This is not very clean and needs improvement, but it is a proof of concept I guess. Let me know if it helped you.

Customfield

declare var Blockly: any;

export class TestField {

    constructor() {
        Blockly.TestField = function (opt_value, opt_validator) {
            opt_value = this.doClassValidation_(opt_value);
            if (opt_value === null) {
                opt_value = Blockly.TestField.DEFAULT_VALUE;
            }  // Else the original value is fine.

            Blockly.TestField.superClass_.constructor.call(this, opt_value, opt_validator);
        };
        Blockly.utils.object.inherits(Blockly.TestField, Blockly.Field);

        Blockly.TestField.fromJson = function (options) {
            const value = Blockly.utils.replaceMessageReferences(options['value']);
            return new Blockly.TestField(value);
        };

        Blockly.fieldRegistry.register('test_field', Blockly.TestField);

    }
}

Usage in Block

this.block.appendDummyInput()
            .appendField(new Blockly.TestField(), 'test_field');

Somewhere the constructor needs to be called to register the field

const TestField = new TestField();

@dpasdar
Copy link
Author

dpasdar commented Nov 19, 2019

Thanks a lot, it works! I will continue developing a use-case and post it as an example here or in the wiki.

@roroettg
Copy link
Owner

I will close this for now. If you want to add sth. you can reopen it.

@evdigitech
Copy link

Hi @dpasdar ,
Can you please provide the example how you developed the logic because I am also getting error of google-closure library(goog)

@dpasdar
Copy link
Author

dpasdar commented Apr 20, 2020

Hi @evdigitech
here is a working example of a custom field to make an auto-complete typeahead text box:

declare var Blockly: any;

export class FieldDataList {
  listOfOptions = [];
  constructor() {
    const that = this;
    Blockly.FieldDataList = function(value, datalist) {
      that.listOfOptions = datalist;
      Blockly.FieldDataList.superClass_.constructor.call(this, value);
    };
    Blockly.utils.object.inherits(Blockly.FieldDataList, Blockly.FieldTextInput);

    Blockly.FieldDataList.fromJson = function(options) {
      const value = Blockly.utils.replaceMessageReferences(options['value']);
      return new Blockly.FieldDataList(value);
    };

    Blockly.TagName = function(tagName) {
      this.tagName_ = tagName;
    };

    Blockly.FieldDataList.prototype.showEditor_ = function(opt_quietInput) {
      this.workspace_ = this.sourceBlock_.workspace;
      const quietInput = opt_quietInput || false;
      if (!quietInput && (Blockly.utils.userAgent.MOBILE ||
        Blockly.utils.userAgent.ANDROID ||
        Blockly.utils.userAgent.IPAD)) {
        this.showPromptEditor_();
      } else {
        Blockly.WidgetDiv.show(
          this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));
        this.isBeingEdited_ = true;
        const div = Blockly.WidgetDiv.DIV;
        const htmlInput = document.createElement('input');
        htmlInput.className = 'blocklyHtmlInput';
        const dataListElement = document.createElement('datalist');
        that.listOfOptions.forEach(o => {
          const option = document.createElement('option');
          option.setAttribute('value', o[0])
          dataListElement.appendChild(option);
        });
        dataListElement.setAttribute('id', 'datalist');
        htmlInput.setAttribute('list', 'datalist');

        div.appendChild(htmlInput);
        div.appendChild(dataListElement);


        htmlInput.value = htmlInput.defaultValue = this.text_;
        this.htmlInput_ = htmlInput;
        this.resizeEditor_();
        this.bindInputEvents_(htmlInput);
        if (!quietInput) {
          htmlInput.focus();
          htmlInput.select();
        }
      }
    };

    Blockly.FieldDataList.TagName = function(tagName) {
      this.tagName_ = tagName;
    };

    Blockly.fieldRegistry.register('test_field', Blockly.FieldDataList);

  };
}

This could be placed anywhere, however we have placed it in the same file as our blockly component.
Here is how to use it elsewhere(in the same file) :

export class SampleBlock extends CustomBlock {
  fields = [];
  constructor(type: string, block: any, blockMutator: BlockMutator, ...args: any[]) {
    super(type, block, blockMutator, ...args);
    this.class = SampleBlock;
    this.fields = this.args[0];
  }

  generateDatalistOptions() {
    const options = [];
    for (const i of this.fields) {
      options.push([i, i]); // first displayname, second var name
    }
    return options;
  }

  defineBlock() {
    this.block.appendDummyInput()
      .appendField('attribute: ')
      .appendField(new Blockly.FieldDataList('typeahead', this.generateDatalistOptions()), 'GETVAR');
    this.block.setOutput(true, 'Input');
    this.block.setColour(130);
    this.block.setTooltip('Select an option!');
    this.block.setHelpUrl('');
  }
  toJavaScriptCode(block: CustomBlock): string | any[] {
    ...
  }
  toXML() {
    return '<block type="some_name"></block>';
  }
  onChange(changeEvent: any) {}
}

Most importantly you should add the following at the end of the said file :

const TestField = new FieldDataList();

(TestField is not used anywhere, but it should be done like that here only to invoke the constructor)

@evdigitech
Copy link

Thank you so much @dpasdar , I have used your code, but getting 2 issues.

  1. when clicking on input then it is showing undefined
  2. I don't want the arrow in input box
    Can you please help me on that, I am sending you the image of my input box.

blockly

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants