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:
- Utwórz kopię istniejącej definicji, w tym generatorów kodu bloku.
- Zmień tekst i nadaj nową nazwę danemu typowi.
- 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? i 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 funkcjiinit
, 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 operatoramiand
ior
. - Wbudowany blok
math_arithmetic
korzysta z menu operatorów+
,-
,×
,÷
i^
.
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
.