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

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

หากต้องการสร้างฟิลด์ใหม่ ให้ทำดังนี้

  1. ใช้ตัวสร้าง
  2. ลงทะเบียนคีย์ JSON และใช้ fromJson
  3. จัดการการเริ่มต้น UI และเครื่องมือฟังเหตุการณ์ on-block
  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 หรือคลาสย่อยรายการใดรายการหนึ่ง) เพื่อเริ่มต้นค่าอย่างถูกต้อง และตั้งค่าเครื่องมือตรวจสอบในเครื่องสำหรับฟิลด์
  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 จะสร้างองค์ประกอบรูปภาพ 1 รายการและองค์ประกอบข้อความ 1 รายการ จากนั้นในระหว่าง 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 ของฟิลด์โดยอัตโนมัติ

การจัดการมูลค่า

→ ดูข้อมูลเกี่ยวกับค่าของฟิลด์เทียบกับข้อความได้ที่โครงสร้างของฟิลด์

ลำดับการตรวจสอบ

โฟลว์ชาร์ตที่อธิบายลำดับการเรียกใช้โปรแกรมตรวจสอบ

การใช้เครื่องมือตรวจสอบคลาส

ฟิลด์ควรยอมรับเฉพาะค่าบางค่า เช่น ช่องตัวเลขควรรับเฉพาะตัวเลข ช่องสีควรรับเฉพาะสี ฯลฯ ซึ่งจะได้รับการตรวจสอบผ่านคลาสและเครื่องมือตรวจสอบในเครื่อง คลาส Validator จะใช้กฎเดียวกันกับ Validator ในเครื่อง ยกเว้นว่าจะเรียกใช้ใน Constructor ด้วย และด้วยเหตุนี้ จึงไม่ควรอ้างอิงบล็อกต้นทาง

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

กรณีการใช้งานที่พบบ่อย 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 คุณอาจต้องระบุฟังก์ชันการซีเรียลไลซ์และการดีซีเรียลไลซ์ (ดูการซีเรียลไลซ์)

การปรับแต่งด้วย 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