In July 2019 (release 2.20190722) a more codified fields API was added. It is intended to be as backwards compatible as possible. This means that if you had created a custom field before July 2019 it will most likely continue to work. Before deciding whether your custom field needs to be upgraded you should read through the Danger areas section, and give your field a thorough testing.
Because there was a lack of standardization between fields before July 2019 it is tricky to cover all changes a developer might need to make. This document tries to cover all likely changes, but if this document does not cover something you are interested in, please read the section on getting upgrade assistance.
Danger areas
Danger areas are known places where the API has changed, and your field could be broken.
Blockly.Field.register
Fields are not longer registered through Blockly.Field.register();
. There is
now a fieldRegistry namespace that handles registration.
Blockly.Field.register('my_field_name', myFieldClass);
Becomes:
Blockly.fieldRegistry.register('my_field_name', myFieldClass);
setText
The setText
function is no longer called by the Blockly core, so if your setText
function contains logic it will need to be moved to the
value handling
suite of functions, the getText
function, and the rendering functions
(depending on what exactly your setText
function is doing).
CustomFields.UpgradeField.prototype.setText = function(newText) {
// Do validation.
if (typeof newText != 'string' || newText === this.text_) {
return;
}
// Fire event.
if (this.sourceBlock_ && Blockly.Events.isEnabled()) {
Blockly.events.fire(new Blockly.Events.BlockChange(
this.sourceBlock_, 'field', this.name, this.text_, newText
));
}
// Update text value.
this.text_ = 'prefix' + newText;
// Rerender.
this.size_.width = 0;
};
Becomes:
CustomFields.UpgradeField.prototype.doClassValidation_ = function(newValue) {
if (typeof newValue != 'string') {
return null;
}
return newValue;
};
CustomFields.UpgradeField.prototype.getText = function() {
return 'prefix' + this.value_;
}
Blockly automatically handles:
- Checking if the new value is different than the old value.
- Updating the value.
- Firing change events.
- Rerendering the field.
You will need to handle:
- Value validation (
doClassValidation_
). - Field text (
getText
). - Field UI
Recommended upgrades
Recommended upgrades are places where the field API has been changed, but if you choose not to make changes your field will most likely still work.
SERIALIZABLE
For more information about the EDITABLE
and SERIALIZABLE
properties see
Editable and serializable properties.
CustomFields.UpgradeField.prototype.SERIALIZABLE = true;
The warning below is ignorable, but you can resolve it by defining the SERIALIZABLE
property:
Detected an editable field that was not serializable. Please define
SERIALIZABLE property as true on all editable custom fields. Proceeding
with serialization.
The warning above means that Blockly believes you want the
field to be serialized (because the EDITABLE
property is true), but
cannot be sure until you define the SERIALIZABLE
property. If you choose to
leave this alone everything will function properly, and your field will be
serialized, but you will receive console warnings.
size_.width
this.size_.width = 0;
Becomes:
this.isDirty_ = true;
The warning below is ignorable, but you can resolve it by setting the isDirty_
property instead of the size_.width
property:
Deprecated use of setting size_.width to 0 to rerender a field. Set
field.isDirty_ to true instead.
The warning above means that Blockly has detected you are using an old method for rerendering a field, and would like you to use the new method.
For more information about the isDirty_
property see
isDirty_.
init
The init
function has been made into a template
function to
reduce duplicate code in subclasses.
CustomFields.UpgradeField.prototype.init = function() {
if (this.fieldGroup_) {
// Already initialized once.
return;
}
// Call superclass.
CustomFields.UpgradeField.superClass_.init.call(this);
// Create DOM elements.
this.extraDom_ = Blockly.utils.dom.createSvgElement('image',
{
'height': '10px',
'width': '10px'
});
this.extraDom_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', 'image.svg');
this.extraDom_.style.cursor = 'pointer';
this.fieldGroup_.appendChild(this.extraDom_);
// Bind events.
this.mouseOverWrapper_ =
Blockly.browserEvents.bind(
this.getClickTarget_(), 'mouseover', this, this.onMouseOver_);
this.mouseOutWrapper_ =
Blockly.browserEvents.bind(
this.getClickTarget_(), 'mouseout', this, this.onMouseOut_);
// Render.
this.setValue(this.getValue());
};
Becomes:
CustomFields.UpgradeField.prototype.initView = function() {
CustomFields.UpgradeField.superClass_.initView.call(this);
this.extraDom_ = Blockly.utils.dom.createSvgElement('image',
{
'height': '10px',
'width': '10px'
});
this.extraDom_.setAttributeNS('http://www.w3.org/1999/xlink',
'xlink:href', 'image.svg');
this.extraDom_.style.cursor = 'pointer';
this.fieldGroup_.appendChild(this.extraDom_);
};
CustomFields.UpgradeField.prototype.bindEvents_ = function() {
CustomFields.UpgradeField.superClass_.bindEvents_.call(this);
this.mouseOverWrapper_ =
Blockly.bindEvent_(
this.getClickTarget_(), 'mouseover', this, this.onMouseOver_);
this.mouseOutWrapper_ =
Blockly.bindEvent_(
this.getClickTarget_(), 'mouseout', this, this.onMouseOut_);
};
This means that blockly now automatically handles:
- Checking if the field is already initialized.
- Creating the
fieldGroup_
. - Rendering the field.
- Binding tooltip and show editor events.
You will need to handle:
- Adding additional
DOM elements (
initView
). - Adding additional
event bindings (
bindEvents_
). - Disposing of event bindings (
dispose
).
onMouseDown_
CustomFields.UpgradeField.prototype.onMouseDown_ = function(e) {
// ...
};
Becomes:
CustomFields.UpgradeField.prototype.showEditor_ = function() {
// ...
}
We recommend that you override the showEditor_
function to handle mouse
clicks rather than the onMouseDown_
function as that allows input to pass
through the gesture system.
For more information on editors see Editors.
setValue
The setValue
function is now a template
function to
reduce duplicate code in subclasses. If your setValue
function contains logic,
consider refactoring it to fit the value handling paths described in
Value handling.
text_
We recommend that you never access or update the text_
property of your
field directly. Instead, use the
getText function
to access the user readable text of your field and the
setValue function
to update the stored value of your field.
For more information about a field's value vs its text see Anatomy of a field.
Getting upgrade assistance
What to provide
When asking for assistance it is best to ask specific questions:
Not recommended: "What's wrong with this field?"
Also not recommended: "Help me upgrade this field."
Recommended: "Field text is not updating properly."
It is also necessary to provide resources to the people assisting you. These files should be easy for others to use.
Not recommended:
- Images of code.
- Incomplete code.
Recommended:
- Complete field code in a text format.
- Images of gifs of bad field behavior.
- Steps to reproduce bad field behavior.
- The version of blockly you are upgrading from.
Where to post
Post upgrade questions on the blockly developer forum.
If you are sure that the issue is a problem with the blockly core you can also post an issue on the blockly GitHub. If you decide to post an issue, please fill out all requested information.