Modifica las definiciones de bloques

Una pregunta frecuente es cómo modificar la definición de un bloque existente. Por ejemplo, es posible que desees agregar una verificación de conexión o cambiar un campo a una entrada de valor.

En general, no es posible modificar una definición de bloque en su lugar.

Cómo modificar una definición existente

Si deseas modificar la definición de un tipo de bloque existente, haz lo siguiente:

  1. Crea una copia de la definición existente, incluidos los generadores de código de bloque.
  2. Modifica la copia y asígnale un nombre nuevo a tu tipo.
  3. Agrega tu nueva definición a Blockly.Blocks.

¿Por qué no puedo modificar directamente una definición existente?

Si quieres saber por qué no puedes modificar una definición existente, sigue leyendo. Consideraremos algunas posibilidades.

Modifica una definición existente directamente

Existen dos maneras de modificar directamente la definición de un bloque existente: la modificación de código y la bifurcación del código. No se recomienda ninguna de las dos opciones, ya que corres el riesgo de dañar el código que depende del código con parches o bifurcado. Ambas técnicas también dificultan la integración de actualizaciones y correcciones de errores. Para obtener más información, consulta ¿Qué sucede con la manipulación de código? y Fork Blockly.

Crea una subclase de una definición existente

Podrías crear una subclase de una definición existente de alguna manera. Lamentablemente, esto no es posible porque las definiciones de bloques no son clases, sino mixins. Por ejemplo, no hay forma de reemplazar una propiedad de color porque la definición no tiene una. En su lugar, tiene una función init que llama a setColour para establecer la propiedad de color en el bloque. Como la llamada está dentro de init, no hay forma de reemplazarla sin reemplazar toda la función init.

Cómo reemplazar una función en una definición existente

Es posible reemplazar una función en una definición existente:

Blockly.Blocks['existing_block'].init = function() {/*new function*/};

Esto funciona, pero debes copiar y modificar la función existente. No puedes reemplazar solo una línea de forma mágica. Esto tiene varios problemas:

  • No es muy diferente de copiar y modificar toda la definición.
  • No puedes usarlo para modificar la función init de los bloques que se definen en JSON, como los bloques integrados de Blockly. Esto se debe a que no hay una función init para copiar, ya que se genera en el tiempo de ejecución.
  • Requiere que definas tu bloque con JavaScript, lo que puede causar problemas con la localización.

Reemplaza los resultados de init

Una forma de "reemplazar solo una línea" de una función init es reemplazar la función init por una que llame a la función init original y, luego, reemplace el resultado de esa llamada. Por ejemplo, el siguiente código cambia el color del bloque logic_null:

const originalInit = Blockly.Blocks['logic_null'].init;
Blockly.Blocks['logic_null'].init = function() {
  originalInit.call(this);
  this.setColour(300);
}

Lamentablemente, esto es menos útil de lo que parece. Por ejemplo:

  • Ampliar las verificaciones de conexión o aplicar un validador de campo menos restrictivo puede invalidar las suposiciones que hacen los generadores de código de bloque y los controladores de eventos.

  • Si reemplazas un campo por una entrada de valor, se dañarán los generadores de código de bloque y los validadores de campo, y es posible que también se dañen los controladores de eventos. También puede ser muy difícil hacerlo para los bloques localizados, ya que las diferentes configuraciones regionales pueden generar bloques con tipos y órdenes diferentes de entradas y campos.

Cómo reemplazar un par clave-valor en una definición JSON

Si el JSON de un bloque está disponible para el público, es posible que se puedan reemplazar valores individuales de JSON. Por ejemplo:

// Block definition.
blockJson = {...};
Blockly.Blocks['my_block'] = {
  init: function() {
    initJson(blockJson); // Called when the block is created.
  }
}

// Third-party code.
blockJson.colour = 100;

Sin embargo, esto solo funciona si el objeto JSON está disponible públicamente, la definición define explícitamente la función init y la función init llama a initJson. No funciona si el JSON se pasa a defineBlocksWithJsonArray o createBlockDefinitionsFromJsonArray porque se procesa antes de que un tercero tenga la oportunidad de modificarlo. (Ten en cuenta que los bloques integrados de Blockly usan createBlockDefinitionsFromJsonArray).

Pero ¿qué sucede si el JSON no se define de esta manera? ¿No debería ser posible reemplazar las propiedades JSON? Lamentablemente, no. La definición contiene una función init (no JSON) y no hay una función para convertir una función init a 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);
};

Diseña para la reutilización

A medida que diseñes tus propios bloques personalizados, es posible que puedas diseñarlos de formas que fomenten la reutilización.

Cómo volver a usar JSON

Si tienes dos bloques que son bastante similares, puedes crear una definición de JSON superior y reutilizarla en las definiciones secundarias. Por ejemplo:

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})
  }
}

Otra alternativa es definir tu JSON en un objeto de acceso público y pasar ese objeto a initJson en tu función init. Esto permite que otras personas reemplacen propiedades individuales. Para obtener más información, consulta Cómo reemplazar un par clave-valor en una definición de JSON.

Cómo volver a usar funciones

Los bloques pueden definir una serie de funciones estándar, como controladores de eventos a nivel del bloque, información sobre herramientas personalizada, validadores de campos y las funciones que usan los modificadores, así como funciones que proporcionan un comportamiento personalizado, como una función que establece valores de campo a partir de datos externos, como la posición actual del brazo de un robot.

Es posible que se puedan volver a usar estas funciones en varios bloques.

Cómo usar un campo desplegable

Si tienes un conjunto de bloques que son prácticamente iguales, excepto por un operador, es posible que puedas diseñar un solo bloque que tenga un campo desplegable para el operador. Por ejemplo:

  • El bloque logic_operation integrado usa un menú desplegable con los operadores and y or.
  • El bloque math_arithmetic integrado usa un menú desplegable con los operadores +, -, ×, ÷ y ^.

Escribir generadores de código para esos bloques suele ser un poco más complejo, pero es más fácil que escribir y mantener varios bloques.

Usa un mutador

Si tienes un conjunto de bloques que representan diferentes variaciones de la misma estructura de programación, es posible que puedas crear un solo bloque que use un mutador. Por ejemplo, el bloque controls_if integrado puede representar varias variaciones de las sentencias if-then-else.