Une question courante est de savoir comment modifier la définition d'un bloc existant. Par exemple, vous pouvez ajouter une vérification de connexion ou convertir un champ en entrée de valeur.
En général, il n'est pas possible de modifier une définition de bloc en place.
Modifier une définition existante
Si vous souhaitez modifier la définition d'un type de bloc existant:
- Créez une copie de la définition existante, y compris des générateurs de code de bloc.
- Modifiez le texte et donnez un nouveau nom à votre type.
- Ajoutez votre nouvelle définition à
Blockly.Blocks
.
Pourquoi ne puis-je pas modifier directement une définition existante ?
Si vous souhaitez savoir pourquoi vous ne pouvez pas modifier une définition existante, lisez la suite. Nous allons examiner quelques possibilités.
Modifier directement une définition existante
Il existe deux façons de modifier directement la définition d'un bloc existant : le monkey-patching et le forking du code. Nous vous déconseillons vivement de procéder de cette manière, car vous risquez de casser le code qui dépend du code modifié ou du code dérivé. Ces deux techniques rendent également difficile l'intégration des mises à jour et des corrections de bugs. Pour en savoir plus, consultez Qu'est-ce que le monkey-patching ? et Dupliquer Blockly.
Créer une sous-classe d'une définition existante
Vous pouvez envisager de créer une sous-classe d'une définition existante. Malheureusement, cela n'est pas possible, car les définitions de bloc ne sont pas des classes, mais des mixins.
Par exemple, il n'est pas possible d'écraser une propriété de couleur, car la définition ne comporte pas de propriété de couleur. À la place, il comporte une fonction init
qui appelle setColour
pour définir la propriété de couleur sur le bloc. Étant donné que l'appel se trouve dans init
, il n'existe aucun moyen de le remplacer sans remplacer l'intégralité de la fonction init
.
Écraser une fonction dans une définition existante
Vous pouvez écraser une fonction dans une définition existante:
Blockly.Blocks['existing_block'].init = function() {/*new function*/};
Cela fonctionne, mais vous devez copier et modifier la fonction existante. Vous ne pouvez pas remplacer une seule ligne par magie. Cette approche pose plusieurs problèmes:
- Cela ne diffère guère de la copie et de la modification de la définition entière.
- Vous ne pouvez pas l'utiliser pour modifier la fonction
init
des blocs définis en JSON, tels que les blocs intégrés de Blockly. En effet, il n'y a pas de fonctioninit
à copier, car elle est générée au moment de l'exécution. - Vous devez définir votre bloc à l'aide de JavaScript, ce qui peut entraîner des problèmes de localisation.
Écraser les résultats d'init
Une façon de "remplacer une seule ligne" d'une fonction init
consiste à remplacer la fonction init
par une fonction qui appelle la fonction init
d'origine, puis écrase le résultat de cet appel. Par exemple, le code suivant modifie la couleur du bloc logic_null
:
const originalInit = Blockly.Blocks['logic_null'].init;
Blockly.Blocks['logic_null'].init = function() {
originalInit.call(this);
this.setColour(300);
}
Malheureusement, cette méthode est moins utile qu'il n'y paraît. Exemple :
Élargir les vérifications de connexion ou appliquer un validateur de champ moins restrictif peut invalider les hypothèses faites par les générateurs de code de bloc et les gestionnaires d'événements.
Remplacer un champ par une valeur saisie casse les générateurs de code de bloc et les validateurs de champ, et peut casser les gestionnaires d'événements. Cela peut également être extrêmement difficile à faire pour les blocs localisés, car différents paramètres régionaux peuvent entraîner des blocs avec des types et des ordres différents d'entrées et de champs.
Remplacer une paire clé-valeur dans une définition JSON
Si le code JSON d'un bloc est publiquement accessible, il est possible d'écraser des valeurs JSON individuelles. Exemple :
// Block definition.
blockJson = {...};
Blockly.Blocks['my_block'] = {
init: function() {
initJson(blockJson); // Called when the block is created.
}
}
// Third-party code.
blockJson.colour = 100;
Toutefois, cela ne fonctionne que si l'objet JSON est accessible au public, que la définition définit explicitement la fonction init
et que la fonction init
appelle initJson
. Cela ne fonctionne pas si le fichier JSON est transmis à defineBlocksWithJsonArray
ou createBlockDefinitionsFromJsonArray
, car il est traité avant qu'un tiers n'ait la possibilité de le modifier. (Notez que les blocs intégrés de Blockly utilisent createBlockDefinitionsFromJsonArray
.)
Mais que faire si le fichier JSON n'est pas défini de cette manière ? Ne devrait-il pas être possible de remplacer les propriétés JSON ? Malheureusement, non. La définition contient une fonction init
(et non JSON), et il n'existe aucune fonction permettant de convertir une fonction init
en 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);
};
Concevoir pour la réutilisation
Lorsque vous concevez vos propres blocs personnalisés, vous pouvez les concevoir de manière à favoriser la réutilisation.
Réutiliser le fichier JSON
Si vous avez deux blocs sensiblement similaires, vous pouvez créer une définition JSON parente et la réutiliser dans les définitions enfants. Exemple :
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})
}
}
Vous pouvez également définir votre fichier JSON dans un objet accessible publiquement et transmettre cet objet à initJson
dans votre fonction init
. Cela permet à d'autres utilisateurs d'écraser des propriétés individuelles. Pour en savoir plus, consultez la section Remplacer une paire clé-valeur dans une définition JSON.
Réutiliser les fonctions
Les blocs peuvent définir un certain nombre de fonctions standards, telles que des gestionnaires d'événements au niveau du bloc, des info-bulles personnalisées, des validateurs de champ et les fonctions utilisées par les mutators, ainsi que des fonctions qui fournissent un comportement personnalisé, comme une fonction qui définit les valeurs de champ à partir de données externes, comme la position actuelle du bras d'un robot.
Il est possible de réutiliser ces fonctions dans plusieurs blocs.
Utiliser un champ déroulant
Si vous disposez d'un ensemble de blocs sensiblement identiques, à l'exception d'un opérateur, vous pouvez concevoir un seul bloc avec un champ déroulant pour l'opérateur. Exemple :
- Le bloc
logic_operation
intégré utilise un menu déroulant avec les opérateursand
etor
. - Le bloc
math_arithmetic
intégré utilise une liste déroulante avec les opérateurs+
,-
,×
,÷
et^
.
Écrire des générateurs de code pour de tels blocs est généralement un peu plus complexe, mais toujours plus facile que d'écrire et de gérer plusieurs blocs.
Utiliser un mutateur
Si vous disposez d'un ensemble de blocs représentant différentes variantes de la même structure de programmation, vous pouvez créer un seul bloc qui utilise un modificateur. Par exemple, le bloc controls_if
intégré peut représenter plusieurs variantes d'instructions if-then-else
.