יצירת סוג חדש של שדה

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

כדי ליצור שדה חדש:

  1. מטמיעים בנאי.
  2. רישום מפתח JSON והטמעה של fromJson.
  3. טיפול באתחול של ממשק המשתמש בבלוקים ושל מאזינים לאירועים.
  4. טיפול בטיפולי סילוק של event listener (בשבילכם, סילוק ה-UI מטופל).
  5. הטמעת טיפול בערך.
  6. מוסיפים ייצוג טקסטי של ערך השדה לצורך נגישות.
  7. מוסיפים עוד פונקציות, כמו:
  8. מגדירים היבטים נוספים של השדה, כמו:

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

דוגמה לשדה בהתאמה אישית מופיעה בהדגמה של שדות מותאמים אישית.

הטמעה של Constructor

בנאי השדה אחראי להגדרת הערך הראשוני של השדה, ואפשר גם להגדיר כלי תיקוף מקומי. ה-build של השדה המותאם אישית מופעל במהלך אתחול חסימת המקור, גם אם חסימת המקור מוגדרת ב-JSON או ב-JavaScript. כך, לשדה בהתאמה אישית אין גישה לבלוק המקור במהלך הבנייה.

דוגמת הקוד הבאה יוצרת שדה מותאם אישית בשם GenericField:

class GenericField extends Blockly.Field {
  constructor(value, validator) {
    super(value, validator);

    this.SERIALIZABLE = true;
  }
}

חתימת השיטה

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

מבנה

הלוגיקה בתוך הבנאי אמורה להיות מבוססת על התהליך הבא:

  1. צריך להפעיל את ה-Super constructor שעבר בירושה (כל השדות המותאמים אישית צריכים לרשת בירושה מ-Blockly.Field או ממחלקות המשנה שלו) כדי לאתחל בצורה נכונה את הערך ולהגדיר את המאמת המקומי לשדה.
  2. אם השדה ניתן להצגה בסדרה, צריך להגדיר את המאפיין המתאים ב-constructor. השדות שניתנים לעריכה צריכים להיות ניתנים לעריכה בסדר טורי, והשדות ניתנים לעריכה כברירת מחדל, לכן סביר להניח שכדאי להגדיר את המאפיין הזה כ-true, אלא אם אתם יודעים שהוא לא אמור להיות ניתן לעריכה בסדרה.
  3. אופציונלי: תוכלו לבצע התאמה אישית נוספת (לדוגמה, Label fields [שדות תווית] מאפשרים העברה של מחלקה מסוג CSS, שאותה המערכת מחילה על הטקסט).

קובץ JSON ורישום

בהגדרות של בלוקים של JSON, השדות מתוארים באמצעות מחרוזת (למשל field_number או field_textinput). השדה הזה שומר באופן מוגבל מפה מהמחרוזות האלה לאובייקטים בשדה, וקורא ל-fromJson לאובייקט המתאים במהלך הבנייה.

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

Blockly.fieldRegistry.register('field_generic', GenericField);

צריך להגדיר גם את הפונקציה fromJson. קודם כל צריך להטמיע בהטמעה את הַכְוונה של כל הפניה מטבלת מחרוזות באמצעות replaceMessageReferences, ואז להעביר את הערכים ל-constructor.

GenericField.fromJson = function(options) {
  const value = Blockly.utils.parsing.replaceMessageReferences(
      options['value']);
  return new CustomFields.GenericField(value);
};

מתבצע אתחול

כשהשדה נבנה, הוא מכיל רק ערך. האתחול הוא המקום שבו נבנה ה-DOM, המודל נוצר (אם בשדה יש מודל) והאירועים קשורים.

תצוגה 'בחסימה'

במהלך האתחול אתם אחראים ליצור את כל מה שתצטרכו בשביל התצוגה בבלוקים של השדה.

ברירות מחדל, רקע וטקסט

פונקציית ברירת המחדל initView יוצרת רכיב rect בצבע בהיר ורכיב text. אם רוצים שהשדה יכלול את שני הרכיבים האלה ועוד כמה הטבות, כדאי לקרוא לפונקציה initView של מחלקת העל לפני שמוסיפים את שאר רכיבי ה-DOM. אם אתם רוצים שהשדה יכלול אחד מהרכיבים האלה, אבל לא את שניהם, אפשר להשתמש בפונקציות createBorderRect_ או createTextElement_.

התאמה אישית של בניית DOM

אם השדה הוא שדה טקסט כללי (למשל קלט טקסט), המערכת תטפל עבורכם ביצירה של DOM. אחרת, צריך לבטל את הפונקציה initView כדי ליצור את רכיבי ה-DOM שנחוצים לכם במהלך הרינדור העתידי של השדה.

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

אפשר ליצור רכיבי DOM באמצעות השיטה Blockly.utils.dom.createSvgElement או באמצעות שיטות יצירת DOM מסורתיות.

הדרישות עבור תצוגה בבלוק של שדה הן:

  • כל רכיבי ה-DOM חייבים להיות צאצאים של fieldGroup_ של השדה. קבוצת השדות נוצרת באופן אוטומטי.
  • כל רכיבי ה-DOM חייבים להישאר בתוך המאפיינים המדווחים בשדה.

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

הוספת סמלי טקסט

אם רוצים להוסיף סמלים לטקסט בשדה (כמו סמל המעלות בשדה Angle), אפשר לצרף את רכיב הסמל (בדרך כלל כלול ב-<tspan>) ישירות ל-textElement_ של השדה.

אירועי קלט

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

bindEvents_() {
  // Call the superclass function to preserve the default behavior as well.
  super.bindEvents_();

  // Then register your own additional event listeners.
  this.mouseDownWrapper_ =
  Blockly.browserEvents.conditionalBind(this.getClickTarget_(), 'mousedown', this,
      function(event) {
        this.originalMouseX_ = event.clientX;
        this.isMouseDown_ = true;
        this.originalValue_ = this.getValue();
        event.stopPropagation();
      }
  );
  this.mouseMoveWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mousemove', this,
      function(event) {
        if (!this.isMouseDown_) {
          return;
        }
        var delta = event.clientX - this.originalMouseX_;
        this.setValue(this.originalValue_ + delta);
      }
  );
  this.mouseUpWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mouseup', this,
      function(_event) {
        this.isMouseDown_ = false;
      }
  );
}

כדי לקשר לאירוע, בדרך כלל צריך להשתמש בפונקציה Blockly.utils.browserEvents.conditionalBind. השיטה הזו של אירועי קישור מסננת נגיעות משניות במהלך גרירה. אם רוצים ש-handler יפעל גם באמצע תהליך גרירה שמתבצעת, אפשר להשתמש בפונקציה Blockly.browserEvents.bind.

השלכה

אם רשמת פונקציות event listener של אירוע בהתאמה אישית בתוך הפונקציה bindEvents_ של השדה, יהיה צורך לבטל את הרישום שלהם בתוך הפונקציה dispose.

אם אתחלתם את התצוגה של השדה בצורה נכונה (על ידי צירוף כל רכיבי ה-DOM ל-fieldGroup_), ה-DOM של השדה ייפטר באופן אוטומטי.

טיפול בערך

← למידע נוסף על הערך של שדה לעומת הטקסט שלו, אפשר לעיין במאמר האנטומיה של שדה.

הזמנה לאימות

תרשים זרימה שמתאר את הסדר שבו פועלים מאמתים

הטמעה של מאמת הכיתה

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

כדי להטמיע את מאמת המחלקות בשדה, צריך לשנות את הפונקציה doClassValidation_.

doClassValidation_(newValue) {
  if (typeof newValue != 'string') {
    return null;
  }
  return newValue;
};

טיפול בערכים חוקיים

אם הערך שהועבר לשדה עם setValue תקין, תקבלו קריאה חוזרת (callback) של doValueUpdate_. כברירת מחדל, הפונקציה doValueUpdate_:

  • מגדיר את המאפיין value_ ל-newValue.
  • מגדיר את המאפיין isDirty_ ל-true.

אם אתם צריכים רק לאחסן את הערך, ואתם לא רוצים לבצע טיפול בהתאמה אישית, אין צורך לעקוף את doValueUpdate_.

אחרת, אם ברצונך לבצע פעולות כמו:

  • אחסון מותאם אישית בנפח newValue.
  • שינוי מאפיינים אחרים בהתבסס על newValue.
  • שמור אם הערך הנוכחי חוקי או לא.

צריך לשנות את doValueUpdate_:

doValueUpdate_(newValue) {
  super.doValueUpdate_(newValue);
  this.displayValue_ = newValue;
  this.isValueValid_ = true;
}

טיפול בערכים לא חוקיים

אם הערך שהעברתם לשדה עם setValue לא חוקי, תקבלו קריאה חוזרת (callback) של doValueInvalid_. כברירת מחדל, הפונקציה doValueInvalid_ לא פועלת. פירוש הדבר הוא שכברירת מחדל, ערכים לא חוקיים לא יוצגו. פירוש הדבר גם שהשדה לא יעובד כי המאפיין isDirty_ לא יוגדר.

כדי להציג ערכים לא חוקיים, יש לשנות את doValueInvalid_. ברוב המקרים, צריך להגדיר את הנכס displayValue_ כערך לא חוקי, להגדיר את isDirty_ כ-true, ולבטל render_ כך שהתצוגה בבלוק תתעדכן על סמך displayValue_ במקום value_.

doValueInvalid_(newValue) {
  this.displayValue_ = newValue;
  this.isDirty_ = true;
  this.isValueValid_ = false;
}

ערכים מרובי חלקים

כשהשדה מכיל ערך מרובה חלקים (למשל רשימות, וקטורים, אובייקטים), כדאי שהמערכת תטפל בחלקים בצורה של ערכים בודדים.

doClassValidation_(newValue) {
  if (FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
    newValue.pattern = null;
  }

  if (FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
    newValue.hat = null;
  }

  if (FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
    newValue.turtleName = null;
  }

  if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
    this.cachedValidatedValue_ = newValue;
    return null;
  }
  return newValue;
}

בדוגמה שלמעלה, כל מאפיין של newValue מאומת בנפרד. לאחר מכן, בסוף הפונקציה doClassValidation_, אם נכס מסוים לא תקין, הערך יישמר במטמון של הנכס cacheValidatedValue_ לפני המחזירה את הערך null (לא חוקי). שמירה במטמון של אובייקט עם מאפיינים שאומתו בנפרד מאפשרת לפונקציה doValueInvalid_ לטפל בהם בנפרד, פשוט באמצעות בדיקה של !this.cacheValidatedValue_.property, במקום לאמת מחדש כל נכס בנפרד.

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

isDirty_

isDirty_ הוא דגל בפונקציה setValue וגם בחלקים אחרים של השדה, שלפיו אפשר לדעת אם צריך לעבד אותו מחדש. אם הערך המוצג בשדה השתנה, צריך בדרך כלל להגדיר את isDirty_ לערך true.

טקסט

← למידע על האופן שבו נעשה שימוש בטקסט בשדה ומה ההבדל בינו לבין ערך השדה, ראו האנטומיה של שדה.

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

getText() {
  let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
  if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
    text += ' hat';
  }
  return text;
}

יצירת עורך

אם תגדירו את הפונקציה showEditor_, Blockly יאזין באופן אוטומטי לזיהוי קליקים ותפעיל את הקריאה showEditor_ בזמן המתאים. אפשר להציג כל HTML בעורך על ידי גלישתו לאחד משני רכיבי div מיוחדים, שנקראים DropDownDiv ו-WidgetDiv, שמרחפים מעל שאר ממשק המשתמש של blockly.

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

תמונה של בוחר הזווית

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

תמונה של עורך קלט הטקסט

showEditor_() {
  // Create the widget HTML
  this.editor_ = this.dropdownCreate_();
  Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);

  // Set the dropdown's background colour.
  // This can be used to make it match the colour of the field.
  Blockly.DropDownDiv.setColour('white', 'silver');

  // Show it next to the field. Always pass a dispose function.
  Blockly.DropDownDiv.showPositionedByField(
      this, this.disposeWidget_.bind(this));
}

קוד לדוגמה של WidgetDiv

showEditor_() {
  // Show the div. This automatically closes the dropdown if it is open.
  // Always pass a dispose function.
  Blockly.WidgetDiv.show(
    this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));

  // Create the widget HTML.
  var widget = this.createWidget_();
  Blockly.WidgetDiv.getDiv().appendChild(widget);
}

ניקוי

גם DropDownDiv וגם WidgetDiv מטפלים בהשחתת רכיבי ה-HTML של הווידג'ט, אבל עליך להשליך באופן ידני כל פונקציות listener שהוחלו על הרכיבים האלה.

widgetDispose_() {
  for (let i = this.editorListeners_.length, listener;
      listener = this.editorListeners_[i]; i--) {
    Blockly.browserEvents.unbind(listener);
    this.editorListeners_.pop();
  }
}

הפונקציה dispose מופעלת בהקשר של null ברכיב DropDownDiv. ב-WidgetDiv היא נקראת בהקשר של WidgetDiv. בכל מקרה, כדאי להשתמש בפונקציה bind כשמעבירים פונקציית סילוק, כפי שמתואר בדוגמאות DropDownDiv ו-WidgetDiv שצוינו למעלה.

← למידע על השלכה של עורכים שאינם ספציפיים להשלכת עורכים, קראו את המאמר השלכה.

עדכון התצוגה על החסימה

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

דוגמאות נפוצות:

  • שינוי הטקסט (תפריט נפתח)
  • שינוי הצבע (צבע)

ברירת מחדל

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

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

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

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

תרשים זרימה המתאר כיצד לקבל החלטה אם לבטל את העיבוד_

התאמה אישית של הרינדור

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

כל השינויים במאפיינים של DOM הם חוקיים, ושני הדברים היחידים שצריך לזכור הם:

  1. צריך לטפל ביצירה של DOM במהלך האתחול, כי הוא יעיל יותר.
  2. תמיד צריך לעדכן את המאפיין size_ כך שיתאים לגודל המסך שבבלוק.
render_() {
  switch(this.value_.hat) {
    case 'Stovepipe':
      this.stovepipe_.style.display = '';
      break;
    case 'Crown':
      this.crown_.style.display = '';
      break;
    case 'Mask':
      this.mask_.style.display = '';
      break;
    case 'Propeller':
      this.propeller_.style.display = '';
      break;
    case 'Fedora':
      this.fedora_.style.display = '';
      break;
  }

  switch(this.value_.pattern) {
    case 'Dots':
      this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
      break;
    case 'Stripes':
      this.shellPattern_.setAttribute('fill', 'url(#stripes)');
      break;
    case 'Hexagons':
      this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
      break;
  }

  this.textContent_.nodeValue = this.value_.turtleName;

  this.updateSize_();
}

מתבצע עדכון של הגודל

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

updateSize_() {
  const bbox = this.movableGroup_.getBBox();
  let width = bbox.width;
  let height = bbox.height;
  if (this.borderRect_) {
    width += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    height += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    this.borderRect_.setAttribute('width', width);
    this.borderRect_.setAttribute('height', height);
  }
  // Note how both the width and the height can be dynamic.
  this.size_.width = width;
  this.size_.height = height;
}

צבעי הבלוקים תואמים

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

applyColour() {
  const sourceBlock = this.sourceBlock_;
  if (sourceBlock.isShadow()) {
    this.arrow_.style.fill = sourceBlock.style.colourSecondary;
  } else {
    this.arrow_.style.fill = sourceBlock.style.colourPrimary;
  }
}

מתבצע עדכון של אפשרות העריכה

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

updateEditable() {
  if (!this.fieldGroup_) {
    // Not initialized yet.
    return;
  }
  super.updateEditable();

  const group = this.getClickTarget_();
  if (!this.isCurrentlyEditable()) {
    group.style.cursor = 'not-allowed';
  } else {
    group.style.cursor = this.CURSOR;
  }
}

עריכה טורית

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

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

אם השדה ניתן לעריכה בסדרה, עליכם להגדיר את המאפיין SERIALIZABLE לערך true.

חברת blockly מספקת שתי קבוצות של קטעי הוק (hooks) לסריאליה עבור שדות. זוג אחד של תגי hook פועל עם מערכת העריכה החדשה של JSON והשני עובד עם המערכת הישנה של סידורי XML.

saveState וגם loadState

saveState ו-loadState הם קטעי הוק (hooks) לסריאליזציה שפועלים עם מערכת הסריאליזציה החדשה של JSON.

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

אחרת, הפונקציה saveState אמורה להחזיר אובייקט/ערך של JSON, שניתן לערוך בו סדרות, שמייצג את מצב השדה. והפונקציה loadState צריכה לקבל את אותו אובייקט/ערך של קובץ JSON שניתן לערוך בו סדרות, ולהחיל אותו על השדה.

saveState() {
  return {
    'country': this.getValue(),  // Value state
    'zoom': this.getZoomLevel(), // UI state
  };
}

loadState(state) {
  this.setValue(state['country']);
  this.setZoomLevel(state['zoom']);
}

סריאליזציה מלאה ונתוני גיבוי

הפרמטר saveState מקבל גם פרמטר אופציונלי doFullSerialization. אפשר להשתמש בפרמטר הזה בשדות שבדרך כלל מפנים למצבים שמוצגים בסדרה על ידי serializer אחר (כמו מודלים של נתוני גיבוי). הפרמטר מסמן שהמצב המצוין בהפניה לא יהיה זמין כשהבלוק עובר תהליך deserialize, ולכן השדה צריך לבצע את כל העריכה בסדרה בעצמו. לדוגמה, זה נכון כאשר בלוק מסוים נמצא בסדרה או כשמתבצעת הדבקה של בלוק מסוים.

שני תרחישים נפוצים לדוגמה:

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

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

saveState(doFullSerialization) {
  const state = {'id': this.variable_.getId()};
  if (doFullSerialization) {
    state['name'] = this.variable_.name;
    state['type'] = this.variable_.type;
  }
  return state;
}

loadState(state) {
  const variable = Blockly.Variables.getOrCreateVariablePackage(
      this.getSourceBlock().workspace,
      state['id'],
      state['name'],   // May not exist.
      state['type']);  // May not exist.
  this.setValue(variable.getId());
}

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

toXml וגם fromXml

toXml ו-fromXml הם קטעי הוק (hooks) של סריאליזציה שפועלים עם מערכת הסריאליזציה הישנה של ה-XML. כדאי להשתמש בקטעים האלה רק אם צריך (למשל, אתם עובדים על בסיס קוד ישן שעדיין לא הועבר). אחרת, כדאי להשתמש ב-saveState וב-loadState.

הפונקציה toXml אמורה להחזיר צומת XML שמייצג את מצב השדה. והפונקציה fromXml אמורה לקבל את אותו צומת XML ולהחיל אותו על השדה.

toXml(fieldElement) {
  fieldElement.textContent = this.getValue();
  fieldElement.setAttribute('zoom', this.getZoomLevel());
  return fieldElement;
}

fromXml(fieldElement) {
  this.setValue(fieldElement.textContent);
  this.setZoomLevel(fieldElement.getAttribute('zoom'));
}

מאפיינים שניתנים לעריכה ולעריכה

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

המאפיין SERIALIZABLE קובע אם השדה יהיה סריאלי. ברירת המחדל היא false. אם המאפיין הזה הוא true, ייתכן שיהיה צורך לספק פונקציות סריאליזציה והתמדה (deserialization). למידע נוסף, ניתן לעיין במאמר הצגה טורית).

התאמה אישית של הסמן

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