ブロック定義を変更する

よくある質問は、既存のブロックの定義を変更する方法です。たとえば、接続チェックを追加したり、フィールドを値入力に変更したりできます。

通常、ブロック定義をその場で変更することはできません。

既存の定義を変更する方法

既存のブロックタイプの定義を変更する場合:

  1. ブロックコード生成ツールを含む既存の定義のコピーを作成します。
  2. コピーを変更し、型に新しい名前を付けます。
  3. 新しい定義を Blockly.Blocks に追加します。

既存の定義を直接変更できないのはなぜですか?

既存の定義を変更できない理由については、以下をご覧ください。いくつかの可能性について検討いたします。

既存の定義を直接変更する

既存のブロックの定義を直接変更する方法は、モンキー パッチとコードのフォークの 2 つがあります。どちらも、モンキーパッチまたはフォークされたコードに依存するコードが破損するリスクがあるため、強くおすすめしません。また、どちらの方法でも、アップデートやバグの修正を統合するのが難しくなります。詳細については、モンキー パッチはどうですか?Blockly をフォークするをご覧ください。

既存の定義のサブクラスを作成する

既存の定義をサブクラス化することを考えたことがあるかもしれません。残念ながら、ブロック定義はクラスではなくミキシンであるため、これは不可能です。たとえば、定義に色プロパティがないため、色プロパティを上書きする方法はありません。代わりに、setColour を呼び出してブロックの色プロパティを設定する init 関数があります。呼び出しは init 内にあるため、init 関数全体を置き換えない限り、呼び出しを置き換える方法はありません。

既存の定義の関数を上書きする

既存の定義の関数を上書きできます。

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

これは機能しますが、既存の関数をコピーして変更する必要があります。1 行だけを魔法のように置き換えることはできません。これにはいくつかの問題があります。

  • これは、定義全体をコピーして変更するのとほとんど同じです。
  • Blockly の組み込みブロックなど、JSON で定義されたブロックの init 関数を変更することはできません。これは、コピーする init 関数がないからです。この関数は実行時に生成されます。
  • JavaScript を使用してブロックを定義する必要があります。これにより、ローカライズに問題が生じる可能性があります。

init の結果を上書きする

init 関数の「1 行だけを置き換える」方法の 1 つは、init 関数を、元の init 関数を呼び出してその呼び出しの結果を上書きする関数に置き換えることです。たとえば、次のコードは logic_null ブロックの色を変更します。

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

残念ながら、これは見た目ほど役に立ちません。次に例を示します。

  • 接続チェックを拡大したり、制限の少ないフィールド バリデータを実装したりすると、ブロックコード生成ツールとイベント ハンドラが行った前提が崩れる可能性があります。

  • フィールドを値入力に置き換えると、ブロックコード生成ツールとフィールド バリデータが破損し、イベント ハンドラが破損する可能性があります。また、ロケールによって入力とフィールドの種類と順序が異なるブロックが作成される可能性があるため、ローカライズされたブロックでは非常に困難になる可能性があります。

JSON 定義内の Key-Value ペアを上書きする

ブロックの 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 を変更する前に 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 を再利用する

ほぼ同じ 2 つのブロックがある場合は、親 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 を定義し、そのオブジェクトを init 関数の initJson に渡すこともできます。これにより、他のユーザーが個々のプロパティを上書きできるようになります。詳細については、JSON 定義で Key-Value ペアを上書きするをご覧ください。

関数を再利用する

ブロックでは、ブロックレベルのイベント ハンドラ、カスタム ツールチップ、フィールド バリデータ、ミューテータで使用される関数などの標準関数を定義できます。また、ロボットのアームの現在の位置などの外部データからフィールド値を設定する関数など、カスタム動作を提供する関数を定義することもできます。

これらの関数はブロック間で再利用できる場合があります。

プルダウン フィールドを使用する

演算子以外はほぼ同じブロックのセットが存在する場合は、演算子のプルダウン フィールドを持つ単一のブロックを設計できます。次に例を示します。

  • 組み込みの logic_operation ブロックは、and 演算子と or 演算子を含むプルダウンを使用します。
  • 組み込みの math_arithmetic ブロックは、+-×÷^ の演算子を含むプルダウンを使用します。

このようなブロックのコード生成ツールの作成は通常、少し複雑ですが、複数のブロックの作成とメンテナンスよりも簡単です。

ミュータを使用する

同じプログラミング構造のさまざまなバリエーションを表す一連のブロックがある場合は、ミュータタを使用する単一のブロックを作成できます。たとえば、組み込みの controls_if ブロックは、if-then-else ステートメントの複数のバリエーションを表すことができます。