יצירת שדה מותאם אישית

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

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

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

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

דוגמה לשדה מותאם אישית זמינה בדמו של שדות מותאמים אישית.

הטמעת קונסטרוקטור

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

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

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

    this.SERIALIZABLE = true;
  }
}

חתימה של method

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

מבנה

הלוגיקה בתוך ה-constructor צריכה לפעול לפי התהליך הבא:

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

JSON והרשמה

בהגדרות של בלוקים ב-JSON, השדות מתוארים במחרוזת (למשל field_number, ‏ field_textinput). ב-Blockly יש מפה מהמחרוזות האלה לאובייקטים של שדות, והמערכת קוראת ל-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

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

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

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

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

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

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

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

אם רוצים להוסיף סמלים לטקסט של שדה (למשל סמל המעלות בשדה 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. שיטת הקישור הזו של אירועים מסננת נגיעות משניות במהלך גרירה. אם רוצים שהטיפול יפעל גם באמצע גרירה, אפשר להשתמש בפונקציה Blockly.browserEvents.bind.

סילוק

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

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

טיפול בערכים

→ מידע על הערך של שדה לעומת הטקסט שלו זמין במאמר המבנה של שדה.

סדר האימות

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

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

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

כדי להטמיע את מאמת הכיתה של השדה, משנים את ברירת המחדל של הפונקציה 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 ולבצע שינוי (override) של 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 של הווידג'ט, אבל צריך להיפטר באופן ידני מכל רכיבי המעקב אחר אירועים שהוספתם לרכיבים האלה.

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 כשמעבירים פונקציית dispose, כפי שמוצג בדוגמאות DropDownDiv ו-WidgetDiv שלמעלה.

→ למידע על ניהול גרסת build שלא ספציפי לעורכים, ראו ניהול גרסת build.

עדכון התצוגה בבלוק

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

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

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

ברירות מחדל

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

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

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

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

saveState וגם loadState

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

במקרים מסוימים אין צורך לספק את הפרטים האלה, כי הטמעות ברירת המחדל יפעלו. אם (1) השדה הוא Subclass ישיר של הכיתה הבסיסית 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. השדה הזה משמש שדות שמפנים בדרך כלל למצב שעבר שרשור (serialization) על ידי סריאליזטור אחר (כמו מודלים של נתוני תמיכה). הפרמטר מאותת שהמצב שאליו מפנה ההפניה לא יהיה זמין כשהבלוק יתבצע סריאליזציה, ולכן השדה צריך לבצע את כל הסריאליזציה בעצמו. לדוגמה, זה נכון כשבלוק בודד עובר סריאליזציה, או כשבלוק מועתק ומוחזר.

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

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

שדה אחד שבו נעשה שימוש באפשרות הזו הוא שדה המשתנה המובנה. בדרך כלל היא מבצעת סריאליזציה של המזהה של המשתנה שאליו היא מפנה, אבל אם הערך של 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 הם ווקרי סריאליזציה שפועלים עם מערכת הסריאליזציה הישנה של 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, יכול להיות שתצטרכו לספק פונקציות של סריאליזציה ושל דה-סריאליזציה (ראו סריאליזציה).

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

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