สร้างช่องที่กำหนดเอง

ก่อนสร้างประเภทช่องใหม่ ให้พิจารณาว่าวิธีการอื่นๆ สำหรับการปรับแต่งช่องใดที่เหมาะกับความต้องการของคุณ หากแอปพลิเคชันต้องจัดเก็บประเภทค่าใหม่ หรือคุณต้องการสร้าง UI ใหม่สำหรับประเภทค่าที่มีอยู่ คุณอาจต้องสร้างประเภทฟิลด์ใหม่

วิธีสร้างช่องใหม่

  1. ใช้คอนสตรคเตอร์
  2. ลงทะเบียนคีย์ JSON และติดตั้งใช้งาน fromJson
  3. จัดการการเริ่มต้น UI ในบล็อกและฟีเจอร์รับฟังเหตุการณ์
  4. จัดการการกำจัด Listener เหตุการณ์ (ระบบจะจัดการการกำจัด UI ให้คุณ)
  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 หรือคลาสย่อยใดคลาสหนึ่งของ Blockly.Field) เพื่อเริ่มต้นค่าอย่างถูกต้องและตั้งค่าโปรแกรมตรวจสอบในพื้นที่สำหรับช่อง
  2. หากช่องเป็นรูปแบบที่นําเข้าได้ ให้ตั้งค่าพร็อพเพอร์ตี้ที่เกี่ยวข้องในคอนสตรคเตอร์ ช่องที่แก้ไขได้ต้องจัดเก็บเป็นอนุกรมได้ และช่องจะแก้ไขได้โดยค่าเริ่มต้น คุณจึงควรตั้งค่าพร็อพเพอร์ตี้นี้เป็น "จริง" เว้นแต่คุณจะทราบว่าไม่ควรจัดเก็บเป็นอนุกรม
  3. ไม่บังคับ: ใช้การปรับแต่งเพิ่มเติม (เช่น ช่องป้ายกำกับอนุญาตให้ส่งคลาส css ซึ่งจะใช้กับข้อความ)

JSON และการลงทะเบียน

ในคําจํากัดความของบล็อก JSON ช่องจะอธิบายด้วยสตริง (เช่น field_number, field_textinput) Blockly จะเก็บแผนที่จากสตริงเหล่านี้ไปยังออบเจ็กต์ฟิลด์ และเรียก fromJson บนออบเจ็กต์ที่เหมาะสมระหว่างการสร้าง

เรียกใช้ Blockly.fieldRegistry.register เพื่อเพิ่มประเภทฟิลด์ลงในแผนที่นี้ โดยส่งคลาสฟิลด์เป็นอาร์กิวเมนต์ที่ 2

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 หากต้องการให้ช่องมีทั้ง 2 รายการนี้ รวมถึงฟีเจอร์เพิ่มเติม ให้เรียกใช้ฟังก์ชัน initView ของซุปเปอร์คลาสก่อนเพิ่มองค์ประกอบ DOM ที่เหลือ หากต้องการให้ช่องมีองค์ประกอบเหล่านี้เพียงอย่างเดียว (ไม่ใช่ทั้ง 2 อย่าง) ให้ใช้ฟังก์ชัน createBorderRect_ หรือ createTextElement_

การปรับแต่งการสร้าง DOM

หากช่องเป็นช่องข้อความทั่วไป (เช่น Text input) ระบบจะจัดการการสร้าง DOM ให้คุณ ไม่เช่นนั้น คุณจะต้องลบล้างฟังก์ชัน initView เพื่อสร้างองค์ประกอบ DOM ที่จําเป็นในระหว่างการแสดงผลช่องในอนาคต

เช่น ช่องแบบเลื่อนลงอาจมีทั้งรูปภาพและข้อความ ใน initView ระบบจะสร้างองค์ประกอบรูปภาพรายการเดียวและองค์ประกอบข้อความรายการเดียว จากนั้นในช่วง render_ ระบบจะแสดงองค์ประกอบที่ใช้งานอยู่และซ่อนองค์ประกอบอื่นๆ โดยอิงตามประเภทของตัวเลือกที่เลือก

การสร้างองค์ประกอบ DOM ทำได้โดยใช้เมธอด Blockly.utils.dom.createSvgElement หรือใช้เมธอดการสร้าง DOM แบบดั้งเดิมก็ได้

ข้อกำหนดของการแสดงในบล็อกของฟิลด์มีดังนี้

  • องค์ประกอบ DOM ทั้งหมดต้องเป็นองค์ประกอบย่อยของ fieldGroup_ ของช่อง ระบบจะสร้างกลุ่มช่องโดยอัตโนมัติ
  • องค์ประกอบ DOM ทั้งหมดต้องอยู่ภายในมิติข้อมูลที่รายงานของช่อง

ดูรายละเอียดเพิ่มเติมเกี่ยวกับการปรับแต่งและการอัปเดตการแสดงผลในบล็อกได้ที่ส่วนการแสดงผล

การเพิ่มสัญลักษณ์ข้อความ

หากต้องการเพิ่มสัญลักษณ์ลงในข้อความของช่อง (เช่น สัญลักษณ์องศาของช่องมุม) คุณสามารถเพิ่มองค์ประกอบสัญลักษณ์ (ปกติจะอยู่ใน <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 ไม่ถูกต้อง คุณจะได้รับ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_ เป็น Flag ที่ใช้ในฟังก์ชัน 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 พิเศษ 1 ใน 2 รายการที่เรียกว่า DropDownDiv และ WidgetDiv ซึ่งจะลอยอยู่เหนือ UI อื่นๆ ของ 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_ฟังก์ชัน

โฟลว์ชาร์ตที่อธิบายวิธีตัดสินใจว่าจะลบล้าง render_ หรือไม่

การปรับแต่งการแสดงผล

หากลักษณะการแสดงผลเริ่มต้นใช้ไม่ได้กับฟิลด์ คุณจะต้องกำหนดลักษณะการแสดงผลที่กำหนดเอง ซึ่งอาจเกี่ยวข้องกับการตั้งค่าข้อความที่แสดงที่กำหนดเอง การเปลี่ยนองค์ประกอบรูปภาพ ไปจนถึงการอัปเดตสีพื้นหลัง

การเปลี่ยนแปลงแอตทริบิวต์ DOM ทั้งหมดนั้นถูกกฎหมาย สิ่งสำคัญ 2 ข้อที่ควรจำไว้มีดังนี้

  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;
  }
}

การจัดรูปแบบข้อมูล

การจัดรูปแบบคือการบันทึกสถานะของช่องเพื่อให้โหลดลงในเวิร์กスペースอีกครั้งในภายหลังได้

สถานะของเวิร์กスペースจะรวมค่าของช่องไว้เสมอ แต่อาจรวมสถานะอื่นๆ ด้วย เช่น สถานะของ UI ของช่อง เช่น หากช่องเป็นแผนที่ที่ซูมได้ซึ่งอนุญาตให้ผู้ใช้เลือกประเทศ คุณก็สามารถจัดรูปแบบระดับการซูมได้ด้วย

หากช่องเป็นรูปแบบอนุกรมได้ คุณต้องตั้งค่าพร็อพเพอร์ตี้ SERIALIZABLE เป็น true

Blockly มีฮุกการจัดรูปแบบชุดข้อมูล 2 ชุดสําหรับช่อง ฮุกคู่หนึ่งจะทํางานกับระบบการจัดรูปแบบ 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 ด้วย ฟิลด์ที่ใช้โดยปกติจะอ้างอิงสถานะที่แปลงเป็นอนุกรมโดยSerializer อื่น (เช่น โมเดลข้อมูลสำรอง) พารามิเตอร์จะส่งสัญญาณว่าสถานะที่อ้างอิงจะไม่พร้อมใช้งานเมื่อมีการแปลงค่าบล็อกกลับ ดังนั้นช่องจึงควรทำการจัดรูปแบบทั้งหมดด้วยตนเอง ตัวอย่างเช่น กรณีนี้จะเกิดขึ้นเมื่อมีการอนุกรมบล็อกแต่ละบล็อก หรือเมื่อมีการคัดลอกและวางบล็อก

กรณีการใช้งานที่พบบ่อย 2 กรณีมีดังนี้

  • เมื่อโหลดบล็อกแต่ละบล็อกลงในเวิร์กスペースที่ไม่มีโมเดลข้อมูลสํารอง ช่องจะมีข้อมูลเพียงพอในสถานะของตนเองเพื่อสร้างโมเดลข้อมูลใหม่
  • เมื่อคัดลอกบล็อกมาวาง ช่องจะสร้างโมเดลข้อมูลสำรองใหม่แทนการอ้างอิงโมเดลที่มีอยู่เสมอ

ช่องที่ใช้รูปแบบนี้คือช่องตัวแปรในตัว โดยปกติแล้ว มันจะจัดรูปแบบรหัสของตัวแปรที่อ้างอิง แต่หาก 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 จะกำหนดว่าช่องควรมี UI เพื่อระบุว่าผู้ใช้โต้ตอบกับช่องนั้นได้หรือไม่ ค่าเริ่มต้นคือ true

พร็อพเพอร์ตี้ SERIALIZABLE จะกำหนดว่าควรจัดรูปแบบฟิลด์หรือไม่ ค่าเริ่มต้นคือ false หากพร็อพเพอร์ตี้นี้เป็น true คุณอาจต้องระบุฟังก์ชันการแปลงเป็นรูปแบบอนุกรมและการแปลงรูปแบบอนุกรม (ดูการแปลงเป็นรูปแบบอนุกรม)

การปรับแต่งเคอร์เซอร์

พร็อพเพอร์ตี้ CURSOR จะกำหนดเคอร์เซอร์ที่ผู้ใช้เห็นเมื่อวางเมาส์เหนือช่องของคุณ โดยต้องเป็นสตริงเคอร์เซอร์ CSS ที่ถูกต้อง โดยค่าเริ่มต้นจะเป็นเคอร์เซอร์ที่กําหนดโดย .blocklyDraggable ซึ่งเป็นเคอร์เซอร์สำหรับจับ