Modyfikowanie definicji bloków

Częstym pytaniem jest, jak zmodyfikować definicję istniejącego bloku. Możesz na przykład dodać sprawdzenie połączenia lub zmienić pole na pole wprowadzania wartości.

Ogólnie nie można modyfikować definicji bloku na miejscu.

Jak zmodyfikować istniejącą definicję

Jeśli chcesz zmodyfikować definicję istniejącego typu bloku:

  1. Utwórz kopię istniejącej definicji, w tym generatorów kodu bloku.
  2. Zmień tekst i nadaj nową nazwę danemu typowi.
  3. Dodaj nową definicję do Blockly.Blocks.

Dlaczego nie mogę bezpośrednio zmodyfikować istniejącej definicji?

Jeśli chcesz się dowiedzieć, dlaczego nie możesz zmodyfikować istniejącej definicji, czytaj dalej. Rozważymy kilka możliwości.

Modyfikowanie istniejącej definicji bezpośrednio

Definicję istniejącego bloku można zmodyfikować na 2 sposoby: za pomocą monkeypatchingu lub forka kodu. Nie zalecamy stosowania obu tych metod, ponieważ istnieje ryzyko, że kod będzie nieprawidłowy. Oba te rozwiązania utrudniają też integrację aktualizacji i poprawek błędów. Więcej informacji znajdziesz w artykule Co to jest monkeypatching?Fork Blockly.

Tworzenie podklasy na podstawie istniejącej definicji

Możesz w jakimś stopniu podklasyfikować istniejącą definicję. Niestety nie jest to możliwe, ponieważ definicje bloków nie są klasami, lecz mieszankami. Nie można na przykład zastąpić właściwości koloru, ponieważ definicja nie ma takiej właściwości. Zamiast tego zawiera ona funkcję init, która wywołuje funkcję setColour, aby ustawić właściwość koloru w bloku. Ponieważ wywołanie znajduje się w funkcji init, nie można go zastąpić bez zastąpienia całej funkcji init.

Zastępowanie funkcji w istniejącej definicji

Funkcję w istniejącej definicji można zastąpić:

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

To działa, ale musisz skopiować i zmodyfikować istniejące polecenie – nie możesz magicznie zastąpić tylko jednej linii. Występują tu 2 problemy:

  • Nie różni się to zbytnio od skopiowania i zmodyfikowania całej definicji.
  • Nie możesz jej używać do modyfikowania funkcji init bloków zdefiniowanych w pliku JSON, takich jak wbudowane bloki Blockly. Dzieje się tak, ponieważ nie ma funkcji init, którą można skopiować – jest ona generowana w czasie wykonywania.
  • Wymaga to zdefiniowania bloku za pomocą JavaScriptu, co może spowodować problemy z lokalizacją.

Zastępowanie wyników init

Jednym ze sposobów „zastąpienia tylko jednej linii” funkcji init jest zastąpienie funkcji init funkcją, która wywołuje oryginalną funkcję init, a następnie zastępuje wynik tego wywołania. Na przykład ten kod zmienia kolor bloku logic_null:

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

Niestety nie jest to tak przydatne, jak się wydaje. Na przykład:

  • Rozszerzenie sprawdzeń dotyczących połączenia lub zastosowanie mniej restrykcyjnego sprawdzania pól może spowodować unieważnienie założeń przyjętych przez generatory kodu blokowego i moduły obsługi zdarzeń.

  • Zastąpienie pola wartością spowoduje uszkodzenie generatorów kodu blokowego i walidatorów pól oraz może uszkodzić moduły obsługi zdarzeń. Może to być też bardzo trudne w przypadku bloków zlokalizowanych, ponieważ różne lokalizacje mogą powodować bloki z różnymi typami i kolejnościami danych wejściowych i pol.

Zastępowanie pary klucz-wartość w definicji pliku JSON

Jeśli plik JSON bloku jest dostępny publicznie, możesz nadpisać poszczególne wartości JSON. Na przykład:

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

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

Działa to jednak tylko wtedy, gdy obiekt JSON jest dostępny publicznie, definicja jednoznacznie definiuje funkcję init, a funkcja init wywołuje funkcję initJson. Nie działa, jeśli dane JSON są przekazywane do funkcji defineBlocksWithJsonArray lub createBlockDefinitionsFromJsonArray, ponieważ są one przetwarzane, zanim zewnętrzna usługa będzie mogła je zmodyfikować. (Pamiętaj, że wbudowane bloki Blockly używają createBlockDefinitionsFromJsonArray).

Co jednak, jeśli dane JSON nie są zdefiniowane w ten sposób? Czy nadal nie można zastąpić właściwości JSON? Niestety, nie. Definicja zawiera funkcję init (nie JSON), a nie ma funkcji, która konwertuje funkcję init na 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);
};

Projektowanie z myślą o ponownym użyciu

Podczas projektowania własnych bloków niestandardowych możesz je zaprojektować w sposób, który ułatwi ich ponowne wykorzystanie.

Ponowne używanie kodu JSON

Jeśli masz 2 bloki, które są do siebie bardzo podobne, możesz utworzyć nadrzędną definicję JSON i używać jej w definicjach podrzędnych. Na przykład:

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

Inną możliwością jest zdefiniowanie danych JSON w dostępnym publicznie obiekcie i przekazanie tego obiektu do funkcji initJson w funkcji init. Dzięki temu inni użytkownicy mogą nadpisać poszczególne właściwości. Więcej informacji znajdziesz w artykule Zastępowanie pary klucz-wartość w definicji JSON.

Ponowne używanie funkcji

Bloki mogą definiować kilka standardowych funkcji, takich jak: funkcje obsługi zdarzeń na poziomie bloku, niestandardowe etykietki narzędzi, funkcje walidacji pól i funkcje używane przez modyfikatory, a także funkcje zapewniające zachowanie niestandardowe, takie jak funkcja ustawiania wartości pól na podstawie danych zewnętrznych, np. bieżąca pozycja ramienia robota.

Możliwe jest ponowne użycie tych funkcji w różnych blokach.

Używanie pola menu

Jeśli masz zestaw bloków, które są w podstawie takie same, z wyjątkiem operatora, możesz zaprojektować jeden blok z polem menu operatora. Na przykład:

  • Wbudowany blok logic_operation korzysta z menu z operatorami andor.
  • Wbudowany blok math_arithmetic korzysta z menu operatorów +, -, ×, ÷^.

Pisanie generatorów kodu dla takich bloków jest zwykle nieco bardziej skomplikowane, ale nadal łatwiejsze niż pisanie i utrzymywanie wielu bloków.

Używanie modyfikatora

Jeśli masz zestaw bloków, które reprezentują różne warianty tej samej struktury programowania, możesz utworzyć jeden blok, który używa mutatora. Na przykład wbudowany blok controls_if może reprezentować wiele odmian stwierdzeń if-then-else.