Распространенный вопрос: как изменить определение существующего блока. Например, вы можете добавить проверку соединения или изменить поле на ввод значения.
Как правило, невозможно изменить определение блока на месте.
Как изменить существующее определение
Если вы хотите изменить определение существующего типа блока:
- Сделайте копию существующего определения, включая генераторы блочного кода.
- Измените копию и дайте вашему типу новое имя.
- Добавьте новое определение в
Blockly.Blocks
.
Почему я не могу напрямую изменить существующее определение?
Если вам интересно, почему вы не можете изменить существующее определение, читайте дальше. Мы рассмотрим некоторые возможности.
Непосредственное изменение существующего определения
Есть два способа напрямую изменить определение существующего блока: исправление обезьян и форк кода. И то, и другое настоятельно не рекомендуется, так как вы рискуете сломать код, который зависит от пропатченного или разветвленного кода обезьяны. Оба метода также затрудняют интеграцию обновлений и исправлений ошибок. Дополнительную информацию см. в разделе «А как насчет «monkeypatching»?». и Форк Блокли .
Подкласс существующего определения
Вам может прийти в голову каким-то образом создать подкласс существующего определения. К сожалению, это невозможно, поскольку определения блоков — это не классы, а миксины. Например, невозможно перезаписать свойство цвета, поскольку в определении нет свойства цвета. Вместо этого у него есть функция init
, которая вызывает setColour
для установки свойства цвета в блоке. Поскольку вызов находится внутри init
, его невозможно заменить без замены всей функции init
.
Перезаписать функцию в существующем определении
Можно перезаписать функцию в существующем определении:
Blockly.Blocks['existing_block'].init = function() {/*new function*/};
Это работает, но вам придется скопировать и изменить существующую функцию — вы не сможете волшебным образом заменить всего одну строку. С этим есть несколько проблем:
- Это мало чем отличается от копирования и изменения всего определения.
- Вы не можете использовать его для изменения функции
init
блоков, определенных в JSON, например встроенных блоков Blockly. Это связано с тем, что нет функцииinit
для копирования — она генерируется во время выполнения. - Требуется определить свой блок с помощью JavaScript, что может вызвать проблемы с локализацией .
Перезаписать результаты init
Один из способов «заменить только одну строку» функции init
— заменить функцию init
функцией, которая вызывает исходную функцию init
, а затем перезаписывает результат этого вызова. Например, следующий код изменяет цвет блока logic_null
:
const originalInit = Blockly.Blocks['logic_null'].init;
Blockly.Blocks['logic_null'].init = function() {
originalInit.call(this);
this.setColour(300);
}
К сожалению, это менее полезно, чем кажется. Например:
Расширение проверок соединения или применение менее ограничительного средства проверки полей может сделать недействительными предположения, сделанные генераторами блочного кода и обработчиками событий.
Замена поля входным значением приведет к поломке генераторов блочного кода и валидаторов полей, а также может привести к поломке обработчиков событий. Это также может быть чрезвычайно сложно сделать для локализованных блоков, поскольку разные локали могут привести к созданию блоков с разными типами и порядками входных данных и полей .
Перезаписать пару ключ-значение в определении JSON.
Если JSON для блока общедоступен, можно перезаписать отдельные значения JSON. Например:
// Block definition.
blockJson = {...};
Blockly.Blocks['my_block'] = {
init: function() {
initJson(blockJson); // Called when the block is created.
}
}
// Third-party code.
blockJson.colour = 100;
Однако это работает только в том случае, если объект JSON общедоступен, определение явно определяет функцию init
, а функция init
вызывает initJson
. Это не работает, если JSON передается в defineBlocksWithJsonArray
или createBlockDefinitionsFromJsonArray
поскольку JSON обрабатывается до того, как третья сторона сможет его изменить. (Обратите внимание, что встроенные блоки Blockly используют createBlockDefinitionsFromJsonArray
.)
Но что, если JSON определен иначе? Разве нельзя по-прежнему перезаписывать свойства JSON? К сожалению, нет. Определение содержит функцию init
(не JSON), и нет функции для преобразования функции init
в JSON.
// Doesn't work. There is no getJson() function.
const json = Blockly.Blocks['existing_block'].getJson();
json['message0'] = 'my new message0';
Blockly.Blocks['existing_block'].init = function () {
initJson(json);
};
Дизайн для повторного использования
Создавая свои собственные блоки, вы можете создавать их таким образом, чтобы обеспечить повторное использование.
Повторное использование JSON
Если у вас есть два блока, которые по существу схожи, вы можете создать родительское определение JSON и повторно использовать его в дочерних определениях. Например:
const parentJson = {
// shared properties
};
Blockly.Blocks['child_block_1'] = {
init: function() {
initJson({...parentJson, colour: 100})
}
}
Blockly.Blocks['child_block_2'] = {
init: function() {
initJson({...parentJson, colour: 200})
}
}
Другой альтернативой является определение вашего JSON в общедоступном объекте и передача этого объекта в initJson
в вашей функции init
. Это позволяет другим пользователям перезаписывать отдельные свойства. Дополнительные сведения см. в разделе Перезапись пары «ключ-значение» в определении JSON .
Функции повторного использования
Блоки могут определять ряд стандартных функций , таких как обработчики событий на уровне блока, пользовательские всплывающие подсказки, средства проверки полей и функции, используемые мутаторами, а также функции, обеспечивающие пользовательское поведение, например функция, которая устанавливает значения полей из внешних данных, таких как текущее положение руки робота.
Возможно, можно повторно использовать эти функции в разных блоках.
Используйте раскрывающееся поле
Если у вас есть набор блоков, которые практически одинаковы, за исключением оператора, вы можете создать один блок с раскрывающимся полем для оператора. Например:
- Встроенный блок
logic_operation
использует раскрывающийся список с операторамиand
иor
. - Встроенный блок
math_arithmetic
использует раскрывающийся список с операторами+
,-
,×
,÷
и^
.
Написание генераторов кода для таких блоков обычно немного сложнее, но все же проще, чем написание и поддержка нескольких блоков.
Используйте мутатор
Если у вас есть набор блоков, представляющих разные варианты одной и той же структуры программирования, вы можете создать один блок, использующий мутатор . Например, встроенный блок controls_if
может представлять несколько вариантов операторов if-then-else
.