إنشاء حقل مخصّص

قبل إنشاء نوع حقل جديد، فكِّر في ما إذا كانت إحدى الطرق الأخرى لتخصيص الحقول تناسب احتياجاتك. إذا كان تطبيقك بحاجة إلى تخزين نوع قيمة جديد، أو إذا كنت تريد إنشاء واجهة مستخدم جديدة لنوع قيمة حالي، من المحتمل أن تحتاج إلى إنشاء نوع حقل جديد.

لإنشاء حقل جديد، اتّبِع الخطوات التالية:

  1. تنفيذ دالة إنشائية
  2. تسجيل مفتاح JSON وتنفيذ fromJson
  3. التعامل مع عملية إعداد واجهة المستخدم الخاصة بالحظر ومعالجات الأحداث
  4. التعامل مع إيقاف أدوات معالجة الأحداث (يتم التعامل مع إيقاف واجهة المستخدم نيابةً عنك).
  5. تنفيذ معالجة القيم:
  6. أضِف تمثيلاً نصيًا لقيمة الحقل لتسهيل الاستخدام.
  7. إضافة وظائف إضافية، مثل:
  8. اضبط جوانب إضافية للحقل، مثل:

يفترض هذا القسم أنّك قرأت محتوى مقالة بنية الحقل وتعرفه جيدًا.

للاطّلاع على مثال على حقل مخصّص، راجِع العرض التوضيحي للحقول المخصّصة.

تنفيذ دالة إنشائية

يكون منشئ الحقل مسؤولاً عن إعداد القيمة الأولية للحقل وإعداد مدقّق محلي اختياريًا. يتم استدعاء أداة إنشاء الحقل المخصّص أثناء عملية تهيئة حزمة المصدر بغض النظر عمّا إذا كانت حزمة المصدر معرَّفة في JSON أو JavaScript. لذلك، لا يمكن للحقل المخصّص الوصول إلى كتلة المصدر أثناء الإنشاء.

ينشئ نموذج الرمز البرمجي التالي حقلًا مخصّصًا باسم GenericField:

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

    this.SERIALIZABLE = true;
  }
}

توقيع الطريقة

تتلقّى دوال إنشاء الحقول عادةً قيمة وأداة تحقق محلية. القيمة اختيارية، وفي حال عدم تمرير قيمة (أو تمرير قيمة لا تجتاز عملية التحقّق من صحة الفئة)، سيتم استخدام القيمة التلقائية للفئة الأصل. بالنسبة إلى الفئة الافتراضية Field، تكون هذه القيمة null. إذا كنت لا تريد استخدام هذه القيمة التلقائية، احرص على إدخال قيمة مناسبة. لا تظهر مَعلمة المدقّق إلا للحقول القابلة للتعديل، ويتم عادةً وضع علامة "اختياري" عليها. يمكنك الاطّلاع على مزيد من المعلومات حول أدوات التحقّق في مستندات أدوات التحقّق.

البنية

يجب أن تتبع منطق الدالة الإنشائية التسلسل التالي:

  1. استدعِ الدالة الإنشائية الفائقة الموروثة (يجب أن ترث جميع الحقول المخصّصة من Blockly.Field أو إحدى فئاتها الفرعية) لتهيئة القيمة بشكل صحيح وضبط أداة التحقّق المحلية للحقل.
  2. إذا كان الحقل قابلاً للتسلسل، اضبط السمة المقابلة في الدالة الإنشائية. يجب أن تكون الحقول القابلة للتعديل قابلة للتسلسل، وتكون الحقول قابلة للتعديل تلقائيًا، لذا من المفترض أن تضبط هذه السمة على "صحيح" ما لم تكن متأكدًا من أنّها غير قابلة للتسلسل.
  3. اختياري: طبِّق تخصيصًا إضافيًا (على سبيل المثال، تسمح حقول التصنيف بتمرير فئة CSS، والتي يتم تطبيقها بعد ذلك على النص).

ملف JSON والتسجيل

في تعريفات كتلة JSON، يتم وصف الحقول بواسطة سلسلة (مثل field_number وfield_textinput). يحتفظ Blockly بخريطة من هذه السلاسل إلى عناصر الحقول، ويستدعي fromJson على العنصر المناسب أثناء الإنشاء.

استدعِ الدالة Blockly.fieldRegistry.register لإضافة نوع الحقل إلى هذه الخريطة، مع تمرير فئة الحقل كمعلَمة ثانية:

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

عليك أيضًا تحديد الدالة fromJson. يجب أن يبدأ التنفيذ بإلغاء الإشارة إلى أي إشارات إلى رموز التوطين باستخدام replaceMessageReferences، ثم تمرير القيم إلى الدالة الإنشائية.

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) داخل الأبعاد المُبلغ عنها للحقل.

راجِع قسم العرض للحصول على مزيد من التفاصيل حول تخصيص العرض على مستوى الحظر وتعديله.

إضافة رموز نصية

إذا أردت إضافة رموز إلى نص حقل (مثل رمز الدرجة في حقل الزاوية)، يمكنك إلحاق عنصر الرمز (المضمّن عادةً في <tspan>) مباشرةً بعنصر textElement_ الخاص بالحقل.

أحداث الإدخال

تسجّل الحقول تلقائيًا أحداث تلميحات الأدوات وأحداث النقر مع الضغط باستمرار على زر الماوس (لاستخدامها في عرض أدوات التعديل). إذا كنت تريد الاستماع إلى أنواع أخرى من الأحداث (على سبيل المثال، إذا كنت تريد التعامل مع السحب والإفلات في حقل)، عليك إلغاء وظيفة 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 صالحة، ستتلقّى ردّ اتصال doValueUpdate_. تلقائيًا، تعمل الدالة doValueUpdate_ على النحو التالي:

  • تضبط هذه السمة قيمة value_ على newValue.
  • تضبط هذه السمة قيمة isDirty_ على true.

إذا كنت تحتاج فقط إلى تخزين القيمة ولا تريد تنفيذ أي معالجة مخصّصة، ليس عليك إلغاء doValueUpdate_.

بخلاف ذلك، إذا كنت تريد تنفيذ إجراءات مثل:

  • مساحة تخزين مخصّصة تبلغ newValue
  • تغيير خصائص أخرى استنادًا إلى newValue
  • احفظ ما إذا كانت القيمة الحالية صالحة أم لا.

عليك تجاهل doValueUpdate_ باتّباع الخطوات التالية:

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

التعامل مع القيم غير الصالحة

إذا كانت القيمة التي تم تمريرها إلى الحقل الذي يتضمّن setValue غير صالحة، ستتلقّى ردّ اتصال 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 في المحرّر من خلال تضمينه في أحد قسمَين خاصَّين، هما 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 في سياق WidgetDiv. في كلتا الحالتين، من الأفضل استخدام الدالة bind عند تمرير دالة dispose، كما هو موضّح في المثالين DropDownDiv وWidgetDiv أعلاه.

→ للحصول على معلومات حول التخلص من العناصر غير المخصّصة للتخلص من المحرِّرين، يُرجى الاطّلاع على التخلص من العناصر.

تعديل العرض على البلوك

يتم استخدام الدالة 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) الحقل الخاص بك هو فئة فرعية مباشرة من الفئة الأساسية 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. يتم استخدام هذا النوع من الحقول عادةً للإشارة إلى حالة تم تسلسلها بواسطة مسلسل مختلف (مثل نماذج البيانات الأساسية). تشير المَعلمة إلى أنّ الحالة المشار إليها لن تكون متاحة عند إلغاء تسلسل الحظر، لذا يجب أن يتولّى الحقل عملية التسلسل بأكملها. على سبيل المثال، يكون هذا صحيحًا عند تسلسل كتلة فردية أو عند نسخ كتلة ولصقها.

في ما يلي حالتا استخدام شائعتان لذلك:

  • عند تحميل جزء فردي في مساحة عمل لا يتوفّر فيها نموذج البيانات الأساسي، يحتوي الحقل على معلومات كافية في حالته الخاصة لإنشاء نموذج بيانات جديد.
  • عند نسخ فقرة ولصقها، ينشئ الحقل دائمًا نموذج بيانات جديدًا بدلاً من الإشارة إلى نموذج حالي.

أحد الحقول التي تستخدم ذلك هو حقل المتغيّر المضمّن. عادةً ما يتم تحويل معرّف المتغيّر الذي تتم الإشارة إليه إلى سلسلة، ولكن إذا كانت قيمة doFullSerialization صحيحة، يتم تحويل جميع حالاته إلى سلسلة.

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، قد تحتاج إلى توفير وظائف التسلسل والتحويل إلى تسلسل (راجِع التسلسل).

التخصيص باستخدام CSS

يمكنك تخصيص الحقل باستخدام CSS. في طريقة initView، أضِف فئة مخصّصة إلى fieldGroup_ في الحقل، ثم أشِر إلى هذه الفئة في CSS.

على سبيل المثال، لاستخدام مؤشر مختلف:

initView() {
  ...

  // Add a custom CSS class.
  if (this.fieldGroup_) {
    Blockly.utils.dom.addClass(this.fieldGroup_, 'myCustomField');
  }
}
.myCustomField {
  cursor: cell;
}

تخصيص المؤشر

تستخدم الصفوف التي توسّع FieldInput تلقائيًا مؤشر text عندما يمرّر المستخدم مؤشر الماوس فوق الحقل، وتستخدم الحقول التي يتم سحبها مؤشر grabbing، وتستخدم جميع الحقول الأخرى مؤشر default. إذا أردت استخدام مؤشر مختلف، يمكنك ضبطه باستخدام CSS.