שינוי הגדרות החסימה

שאלה נפוצה היא איך משנים את ההגדרה של בלוק קיים. לדוגמה, תוכלו להוסיף בדיקת חיבור או לשנות שדה להזנת ערך.

באופן כללי, אי אפשר לשנות הגדרת בלוק במקום.

איך משנים הגדרה קיימת

כדי לשנות את ההגדרה של סוג בלוק קיים:

  1. יוצרים עותק של ההגדרה הקיימת, כולל הגנרטורים של קוד בלוקים.
  2. משנים את העותק ומעניקים לסוג שם חדש.
  3. מוסיפים את ההגדרה החדשה ל-Blockly.Blocks.

למה אי אפשר לשנות הגדרה קיימת ישירות?

רוצים לדעת למה אי אפשר לשנות הגדרה קיימת? כדאי להמשיך לקרוא. נבחן כמה אפשרויות.

שינוי ישיר של הגדרה קיימת

יש שתי דרכים לשנות ישירות את ההגדרה של בלוק קיים: תיקון קוד (monkeypatching) ויצירת ענף (fork) של הקוד. לא מומלץ לעשות את אף אחת מהפעולות האלה, כי אתם עלולים לשבור קוד שמבוסס על הקוד שהוחלף או על הקוד שהופרד. שתי השיטות האלה גם מקשות על שילוב עדכונים ותיקוני באגים. למידע נוסף, ראו מה לגבי תיקון קוד בזמן ריצה (monkeypatching)? ויצירת פורק (fork) של Blockly.

יצירת תת-סוג של הגדרה קיימת

יכול להיות שתבחרו ליצור תת-סוג של הגדרה קיימת. לצערנו, אי אפשר לעשות זאת כי הגדרות של בלוקים הן לא כיתות – הן 'מיקסים'. לדוגמה, אי אפשר לשנות את ערך המאפיין 'צבע' כי להגדרה אין מאפיין 'צבע'. במקום זאת, יש לו פונקציה init שמפעילה את setColour כדי להגדיר את מאפיין הצבע בבלוק. מכיוון שהקריאה נמצאת בתוך init, אי אפשר להחליף אותה בלי להחליף את כל פונקציית init.

החלפת פונקציה בהגדרה קיימת

אפשר לשכתב פונקציה בהגדרה קיימת:

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

אפשר לעשות זאת, אבל צריך להעתיק ולשנות את הפונקציה הקיימת – אי אפשר להחליף רק שורה אחת באופן קסום. יש לכך כמה בעיות:

  • זה לא שונה בהרבה מהעתקה ושינוי של ההגדרה כולה.
  • אי אפשר להשתמש בו כדי לשנות את הפונקציה init של בלוקים שמוגדרים ב-JSON, כמו הבלוקים המובנים של Blockly. הסיבה לכך היא שאין פונקציית init שאפשר להעתיק – היא נוצרת בזמן הריצה.
  • כדי להשתמש ב-JavaScript, צריך להגדיר את הבלוק באמצעות JavaScript, וזה עלול לגרום לבעיות בלוקליזציה.

החלפת התוצאות של init

אחת הדרכים "להחליף רק שורה אחת" של פונקציית init היא להחליף את הפונקציה init בפונקציה שמפעילה את פונקציית init המקורית ואז מחליפה את התוצאה של הקריאה הזו. לדוגמה, הקוד הבא משנה את הצבע של הבלוק logic_null:

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

לצערנו, האפשרות הזו פחות מועילה ממה שנדמה. לדוגמה:

  • הרחבת הבדיקות של החיבורים או החלת מאמת שדות פחות מגביל עלול לגרום לביטול ההנחות של הגנרטורים של קוד הבלוק ושל הגורמים המטפלים באירועים.

  • החלפת שדה בהזנת ערך תגרום לשיבושים ביוצרי קוד בלוקים ובמאמתי שדות, ויכול להיות שתגרום לשיבושים בגורמים מטפלים באירועים. יכול להיות גם שיהיה קשה מאוד לעשות זאת בבלוקים מותאמים לאזור גיאוגרפי, כי אזורים גיאוגרפיים שונים עשויים לגרום לבלוק עם סוגים שונים של קלט ושדות, ובסדרים שונים.

החלפת ערך של צמד מפתח/ערך בהגדרת JSON

אם קובץ ה-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 מעובד לפני שלצד השלישי יש הזדמנות לשנות אותו. (שימו לב שהבלוקים המובנים של 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

אם יש לכם שני בלוקים דומים במידה רבה, תוכלו ליצור הגדרת 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 באובייקט שגלוי לכולם ולהעביר את האובייקט הזה אל initJson בפונקציה init. כך אנשים אחרים יוכלו לשנות את הערכים של נכסים ספציפיים. מידע נוסף זמין במאמר החלפת ערך של צמד מפתח/ערך בהגדרת JSON.

שימוש חוזר בפונקציות

בבלוק אפשר להגדיר מספר פונקציות רגילות, כמו רכיבי טיפול באירועים ברמת הבלוק, טיפים מותאמים אישית, מאמתי שדות והפונקציות שבהן משתמשים המטמורפיים, וגם פונקציות שמספקות התנהגות מותאמת אישית, כמו פונקציה שמגדירה את ערכי השדות מנתונים חיצוניים, כמו המיקום הנוכחי של הזרוע של הרובוט.

יכול להיות שאפשר יהיה לעשות שימוש חוזר בפונקציות האלה בבלוק אחר.

שימוש בשדה תפריט נפתח

אם יש לכם קבוצה של בלוקים שדומים זה לזה באופן משמעותי, מלבד המפעיל, יכול להיות שתוכלו לעצב בלוק אחד עם שדה נפתח למפעיל. לדוגמה:

  • בבלוק המובנה logic_operation נעשה שימוש בתפריט נפתח עם האופרטורים and ו-or.
  • בבלוק המובנה math_arithmetic נעשה שימוש בתפריט נפתח עם האופרטורים +,‏ -,‏ ×,‏ ÷ ו-^.

כתיבת גנרטורים של קוד לבלוקים כאלה היא בדרך כלל קצת מורכבת יותר, אבל עדיין קלה יותר מכתיבת וניהול של כמה בלוקים.

שימוש במוטאטור

אם יש לכם קבוצה של בלוקים שמייצגים וריאציות שונות של אותו מבנה תכנות, יכול להיות שתוכלו ליצור בלוק יחיד שמשתמש במוטאטור. לדוגמה, הבלוק המובנה controls_if יכול לייצג כמה וריאציות של משפטי if-then-else.