Blockdefinitionen ändern

Eine häufige Frage ist, wie die Definition eines vorhandenen Blocks geändert werden kann. Sie können beispielsweise eine Verbindungsüberprüfung hinzufügen oder ein Feld in eine Werteingabe ändern.

Im Allgemeinen ist es nicht möglich, eine Blockdefinition vor Ort zu ändern.

Vorhandene Definitionen ändern

So ändern Sie die Definition eines vorhandenen Blocktyps:

  1. Erstellen Sie eine Kopie der vorhandenen Definition, einschließlich Blockcodegeneratoren.
  2. Ändern Sie den Text und geben Sie dem Typ einen neuen Namen.
  3. Fügen Sie Blockly.Blocks die neue Definition hinzu.

Warum kann ich eine vorhandene Definition nicht direkt ändern?

Im Folgenden erfahren Sie, warum Sie eine vorhandene Definition nicht ändern können. Wir werden einige Möglichkeiten prüfen.

Vorhandene Definition direkt ändern

Es gibt zwei Möglichkeiten, die Definition eines vorhandenen Blocks direkt zu ändern: Monkeypatching und Fork des Codes. Beides wird dringend abgeraten, da das Risiko besteht, dass Code, der vom Monkeypatch oder Fork abhängt, beschädigt wird. Beide Methoden erschweren auch die Integration von Updates und Fehlerkorrekturen. Weitere Informationen finden Sie unter Was ist Monkeypatching? und Blockly forken.

Unterklasse einer vorhandenen Definition

Vielleicht möchten Sie eine vorhandene Definition auf irgendeine Weise unterteilen. Leider ist das nicht möglich, da Blockdefinitionen keine Klassen, sondern Mixins sind. Es ist beispielsweise nicht möglich, eine Farbeigenschaft zu überschreiben, da die Definition keine Farbeigenschaft hat. Stattdessen gibt es eine init-Funktion, die setColour aufruft, um die Farbeigenschaft für den Block festzulegen. Da sich der Aufruf in init befindet, kann er nicht ersetzt werden, ohne die gesamte init-Funktion zu ersetzen.

Eine Funktion in einer vorhandenen Definition überschreiben

Es ist möglich, eine Funktion in einer vorhandenen Definition zu überschreiben:

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

Das funktioniert, aber Sie müssen die vorhandene Funktion kopieren und ändern. Sie können nicht einfach eine Zeile ersetzen. Das hat mehrere Nachteile:

  • Das ist nicht viel anders als die gesamte Definition zu kopieren und zu ändern.
  • Sie können damit nicht die init-Funktion von Blöcken ändern, die in JSON definiert sind, z. B. die integrierten Blöcke von Blockly. Das liegt daran, dass es keine init-Funktion zum Kopieren gibt. Sie wird zur Laufzeit generiert.
  • Sie müssen den Block mit JavaScript definieren, was Probleme bei der Lokalisierung verursachen kann.

Ergebnisse von init überschreiben

Eine Möglichkeit, „nur eine Zeile“ einer init-Funktion zu ersetzen, besteht darin, die init-Funktion durch eine Funktion zu ersetzen, die die ursprüngliche init-Funktion aufruft und dann das Ergebnis dieses Aufrufs überschreibt. Mit dem folgenden Code wird beispielsweise die Farbe des logic_null-Blocks geändert:

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

Leider ist das weniger nützlich, als es scheint. Beispiel:

  • Wenn Sie Verbindungsüberprüfungen erweitern oder einen weniger restriktiven Feldvalidierer anwenden, werden möglicherweise Annahmen von Blockcodegeneratoren und Ereignis-Handlern ungültig.

  • Wenn Sie ein Feld durch eine Werteingabe ersetzen, funktionieren Blockcodegeneratoren und Feldvalidierer nicht mehr und möglicherweise auch Ereignishandler nicht. Bei lokalisierten Blöcken kann das auch extrem schwierig sein, da unterschiedliche Gebietsschemata zu Blöcken mit unterschiedlichen Typen und Reihenfolgen von Eingaben und Feldern führen können.

Schlüssel/Wert-Paar in einer JSON-Definition überschreiben

Wenn das JSON für einen Block öffentlich zugänglich ist, ist es unter Umständen möglich, einzelne JSON-Werte zu überschreiben. Beispiel:

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

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

Dies funktioniert jedoch nur, wenn das JSON-Objekt öffentlich zugänglich ist, die init-Funktion in der Definition explizit definiert ist und die init-Funktion initJson aufruft. Das funktioniert nicht, wenn die JSON-Datei an defineBlocksWithJsonArray oder createBlockDefinitionsFromJsonArray übergeben wird, da sie verarbeitet wird, bevor ein Drittanbieter die Möglichkeit hat, sie zu ändern. Hinweis: Die integrierten Blöcke von Blockly verwenden createBlockDefinitionsFromJsonArray.

Was ist aber, wenn die JSON nicht so definiert ist? Sollte es nicht weiterhin möglich sein, JSON-Properties zu überschreiben? Leider nicht. Die Definition enthält eine init-Funktion (keine JSON-Funktion) und es gibt keine Funktion, mit der eine init-Funktion in JSON konvertiert werden kann.

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

Design für die Wiederverwendung

Wenn Sie Ihre eigenen benutzerdefinierten Blöcke entwerfen, können Sie sie so gestalten, dass sie wiederverwendet werden können.

JSON wiederverwenden

Wenn Sie zwei Blöcke haben, die im Wesentlichen identisch sind, können Sie eine übergeordnete JSON-Definition erstellen und diese in untergeordneten Definitionen wiederverwenden. Beispiel:

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

Eine weitere Möglichkeit besteht darin, Ihr JSON in einem öffentlich zugänglichen Objekt zu definieren und dieses Objekt in Ihrer init-Funktion an initJson zu übergeben. So können andere Nutzer einzelne Properties überschreiben. Weitere Informationen finden Sie unter Schlüssel/Wert-Paar in einer JSON-Definition überschreiben.

Funktionen wiederverwenden

Für Blöcke können eine Reihe von Standardfunktionen definiert werden, z. B. Ereignishandler auf Blockebene, benutzerdefinierte Kurzinfos, Feldvalidierer und die von Mutatatoren verwendeten Funktionen. Außerdem können Funktionen für benutzerdefiniertes Verhalten definiert werden, z. B. eine Funktion, die Feldwerte aus externen Daten festlegt, z. B. die aktuelle Position des Arms eines Roboters.

Es ist unter Umständen möglich, diese Funktionen für mehrere Blöcke zu verwenden.

Drop-down-Feld verwenden

Wenn Sie mehrere Blöcke haben, die bis auf einen Operator im Wesentlichen identisch sind, können Sie einen einzelnen Block mit einem Drop-down-Feld für den Operator entwerfen. Beispiel:

  • Der integrierte Block logic_operation verwendet ein Drop-down-Menü mit den Operatoren and und or.
  • Der integrierte Block math_arithmetic verwendet ein Drop-down-Menü mit den Operatoren +, -, ×, ÷ und ^.

Das Erstellen von Codegeneratoren für solche Blöcke ist in der Regel etwas komplexer, aber immer noch einfacher als das Erstellen und Pflegen mehrerer Blöcke.

Mutator verwenden

Wenn Sie mehrere Blöcke haben, die verschiedene Varianten derselben Programmierstruktur darstellen, können Sie möglicherweise einen einzelnen Block mit einem Mutator erstellen. Der integrierte controls_if-Block kann beispielsweise mehrere Varianten von if-then-else-Anweisungen darstellen.