การสร้างประเภทช่องใหม่

ก่อนสร้างประเภทช่องใหม่ ให้พิจารณาดูว่ามีวิธีอื่นๆ ในการปรับแต่งช่องให้เหมาะกับความต้องการของคุณหรือไม่ หากแอปพลิเคชันของคุณจำเป็นต้องจัดเก็บประเภทค่าใหม่ หรือคุณต้องการสร้าง 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 หรือคลาสย่อย) เพื่อเริ่มต้นค่าอย่างถูกต้องและตั้งค่าโปรแกรมตรวจสอบภายในสำหรับฟิลด์ของคุณ
  2. หากช่องทำให้เป็นอนุกรมได้ ให้ตั้งค่าพร็อพเพอร์ตี้ที่สอดคล้องกันในตัวสร้าง ช่องที่แก้ไขได้จะต้องเป็นแบบทำให้เป็นอนุกรมได้ และช่องดังกล่าวจะแก้ไขได้โดยค่าเริ่มต้น คุณจึงควรตั้งค่าพร็อพเพอร์ตี้นี้เป็น "จริง" เว้นแต่คุณจะทราบว่าไม่ควรทำให้เป็นอนุกรมได้
  3. ไม่บังคับ: ใช้การปรับแต่งเพิ่มเติม (เช่น ช่องป้ายกำกับทำให้สามารถส่งคลาส CSS ซึ่งจากนั้นจะนำไปใช้กับข้อความ)

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

ในการกำหนดบล็อก JSON ช่องจะอธิบายด้วยสตริง (เช่น field_number, field_textinput) บล็อกจะดูแลแผนที่จากสตริงเหล่านี้ไปยังออบเจ็กต์ในช่อง และเรียกใช้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 ใน Superclass ก่อนเพิ่มองค์ประกอบ DOM ที่เหลือ หากต้องการให้ช่องมีองค์ประกอบ 1 รายการ แต่ไม่ใช่ทั้ง 2 องค์ประกอบ ให้ใช้ฟังก์ชัน createBorderRect_ หรือ createTextElement_

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

หากช่องของคุณเป็นช่องข้อความทั่วไป (เช่น อินพุตข้อความ) เราจะจัดการการสร้าง 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 ได้

การกำจัด

หากลงทะเบียน Listener เหตุการณ์ที่กำหนดเองภายในฟังก์ชัน 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 และลบล้างการแสดงผล_ เพื่อให้การแสดงผลในบล็อกอัปเดตตาม 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 พิเศษ 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 ระบบจะเรียกใช้ URL นี้ในบริบทของ WidgetDiv ไม่ว่ากรณีใด ก็ควรใช้ฟังก์ชัน bind เมื่อส่งฟังก์ชันการกำจัด ดังที่แสดงในตัวอย่าง DropDownDiv และ WidgetDiv ด้านบน

→ สำหรับข้อมูลเกี่ยวกับการกำจัดไฟล์ที่ไม่ได้มีไว้สำหรับการกำจัดเอดิเตอร์โดยเฉพาะ โปรดดูการกำจัด

กำลังอัปเดตจอแสดงผลในบล็อก

ใช้ฟังก์ชัน render_ เพื่ออัปเดตการแสดงผลบนบล็อกของช่องให้ตรงกับค่าภายใน

ตัวอย่างทั่วไปมีดังนี้

  • เปลี่ยนข้อความ (เมนูแบบเลื่อนลง)
  • เปลี่ยนสี (สี)

ค่าเริ่มต้น

ฟังก์ชัน render_ เริ่มต้นจะตั้งค่าข้อความที่แสดงตามผลลัพธ์ของฟังก์ชัน getDisplayText_ ฟังก์ชัน getDisplayText_ จะแสดงผลพร็อพเพอร์ตี้ value_ ของช่องที่แคสต์ไปยังสตริง หลังจากที่มีการตัดข้อความตามความยาวสูงสุดของข้อความ

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