Tạo loại trường mới

Trước khi tạo loại trường mới, hãy cân nhắc xem một trong những phương thức khác để tuỳ chỉnh trường có phù hợp với nhu cầu của bạn hay không. Nếu ứng dụng của bạn cần lưu trữ một loại giá trị mới hoặc bạn muốn tạo giao diện người dùng mới cho một loại giá trị hiện có, thì có thể bạn sẽ cần tạo một loại trường mới.

Để tạo trường mới, hãy làm như sau:

  1. Triển khai hàm khởi tạo.
  2. Đăng ký khoá JSON và triển khai fromJson.
  3. Xử lý việc khởi chạy giao diện người dùng trong khối và trình nghe sự kiện.
  4. Xử lý việc loại bỏ trình nghe sự kiện (việc loại bỏ giao diện người dùng được xử lý cho bạn).
  5. Triển khai quá trình xử lý giá trị.
  6. Thêm bản trình bày dạng văn bản cho giá trị của trường để hỗ trợ tiếp cận.
  7. Thêm các chức năng bổ sung như:
  8. Định cấu hình các thành phần khác của trường, chẳng hạn như:

Phần này giả định rằng bạn đã đọc và quen với nội dung trong Cấu tạo của một trường.

Để biết ví dụ về trường tuỳ chỉnh, hãy xem Bản minh hoạ trường tuỳ chỉnh.

Triển khai hàm khởi tạo

Hàm khởi tạo của trường chịu trách nhiệm thiết lập giá trị ban đầu của trường và tuỳ ý thiết lập một trình xác thực cục bộ. Hàm khởi tạo của trường tuỳ chỉnh được gọi trong quá trình khởi chạy khối nguồn bất kể khối nguồn được xác định trong JSON hay JavaScript. Vì vậy, trường tuỳ chỉnh không có quyền truy cập vào khối nguồn trong quá trình xây dựng.

Mã mẫu sau đây sẽ tạo một trường tuỳ chỉnh có tên GenericField:

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

    this.SERIALIZABLE = true;
  }
}

Chữ ký phương thức

Hàm khởi tạo trường thường nhận một giá trị và trình xác thực cục bộ. Giá trị là không bắt buộc và nếu bạn không truyền giá trị (hoặc bạn truyền một giá trị không xác thực được lớp), thì giá trị mặc định của siêu lớp sẽ được sử dụng. Đối với lớp Field mặc định, giá trị đó là null. Nếu bạn không muốn giá trị mặc định đó, hãy nhớ chuyển giá trị phù hợp. Tham số trình xác thực chỉ xuất hiện cho các trường có thể chỉnh sửa và thường được đánh dấu là không bắt buộc. Hãy tìm hiểu thêm về trình xác thực trong tài liệu về Validators.

Cấu trúc

Logic bên trong hàm khởi tạo của bạn phải tuân theo luồng sau:

  1. Gọi hàm khởi tạo cấp cao kế thừa (tất cả các trường tuỳ chỉnh phải kế thừa từ Blockly.Field hoặc một trong các lớp con của nó) để khởi chạy đúng giá trị và đặt trình xác thực cục bộ cho trường của bạn.
  2. Nếu trường của bạn có thể chuyển đổi tuần tự, hãy đặt thuộc tính tương ứng trong hàm khởi tạo. Các trường có thể chỉnh sửa phải chuyển đổi tuần tự được và các trường có thể chỉnh sửa theo mặc định. Vì vậy, bạn có thể đặt thuộc tính này thành true (đúng) trừ phi bạn biết rằng thuộc tính này không được tuần tự hoá.
  3. Không bắt buộc: Áp dụng chế độ tuỳ chỉnh bổ sung (ví dụ: Các trường nhãn cho phép truyền một lớp css, sau đó sẽ áp dụng cho văn bản).

JSON và quy trình đăng ký

Trong định nghĩa khối JSON, các trường được mô tả bằng một chuỗi (ví dụ: field_number, field_textinput). Duy trì một cách khối ánh xạ từ các chuỗi này đến các đối tượng trường và gọi fromJson trên đối tượng thích hợp trong quá trình xây dựng.

Gọi Blockly.fieldRegistry.register để thêm loại trường của bạn vào bản đồ này, truyền vào lớp trường làm đối số thứ hai:

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

Bạn cũng cần xác định hàm fromJson. Trước tiên, quá trình triển khai của bạn nên huỷ tham chiếu mọi bảng chuỗi bằng cách sử dụng replaceMessageReferences, sau đó chuyển các giá trị đến hàm khởi tạo.

GenericField.fromJson = function(options) {
  const value = Blockly.utils.parsing.replaceMessageReferences(
      options['value']);
  return new CustomFields.GenericField(value);
};

Đang khởi tạo

Khi được tạo, trường của bạn về cơ bản chỉ chứa giá trị. Khởi tạo là nơi tạo DOM, mô hình được tạo (nếu trường sở hữu mô hình) và các sự kiện được liên kết.

Hiển thị trên danh sách chặn

Trong quá trình khởi chạy, bạn chịu trách nhiệm tạo mọi thứ bạn cần để hiển thị theo khối của trường.

Mặc định, nền và văn bản

Hàm initView mặc định tạo phần tử rect có màu sáng và phần tử text. Nếu bạn muốn trường của mình có cả hai nội dung này và một số tính năng khác, hãy gọi hàm initView của lớp cao cấp trước khi thêm các phần tử DOM còn lại. Nếu muốn trường của mình có một (nhưng không phải cả hai) phần tử này, bạn có thể sử dụng các hàm createBorderRect_ hoặc createTextElement_.

Tuỳ chỉnh cấu trúc DOM

Nếu trường của bạn là một trường văn bản chung (ví dụ: Đầu vào văn bản), thì quá trình tạo DOM sẽ được xử lý cho bạn. Nếu không, bạn sẽ phải ghi đè hàm initView để tạo các phần tử DOM mà bạn cần trong quá trình kết xuất trường trong tương lai.

Ví dụ: một trường thả xuống có thể chứa cả hình ảnh và văn bản. Trong initView, hàm này sẽ tạo một phần tử hình ảnh và một phần tử văn bản. Sau đó, trong render_, phần tử đang hoạt động sẽ hiện và ẩn phần tử còn lại dựa trên loại của tuỳ chọn đã chọn.

Bạn có thể tạo các phần tử DOM bằng cách sử dụng phương thức Blockly.utils.dom.createSvgElement hoặc sử dụng phương thức tạo DOM truyền thống.

Các yêu cầu đối với tính năng hiển thị theo khối của một trường là:

  • Tất cả các phần tử DOM phải là phần tử con của fieldGroup_ của trường. Nhóm trường được tạo tự động.
  • Tất cả các phần tử DOM phải nằm trong phương diện được báo cáo của trường.

Hãy xem phần Kết xuất để biết thêm thông tin chi tiết về cách tuỳ chỉnh và cập nhật chế độ hiển thị trên khối.

Thêm ký hiệu văn bản

Nếu muốn thêm ký hiệu vào văn bản của một trường (chẳng hạn như biểu tượng độ của trường Góc), bạn có thể thêm trực tiếp phần tử biểu tượng đó (thường có trong <tspan>) vào textElement_ của trường đó.

Sự kiện nhập

Theo mặc định, các trường sẽ đăng ký sự kiện chú giải công cụ và sự kiện di chuột xuống (dùng để hiển thị trình chỉnh sửa). Nếu muốn theo dõi các loại sự kiện khác (ví dụ: nếu bạn muốn xử lý thao tác kéo trên một trường), bạn nên ghi đè hàm bindEvents_ của trường đó.

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

Thông thường, để liên kết với một sự kiện, bạn nên dùng hàm Blockly.utils.browserEvents.conditionalBind. Phương thức này của các sự kiện liên kết lọc ra các thao tác chạm phụ trong khi kéo. Nếu muốn trình xử lý có thể chạy ngay cả khi đang kéo, bạn có thể sử dụng hàm Blockly.browserEvents.bind.

Loại bỏ

Nếu bạn đã đăng ký bất kỳ trình nghe sự kiện tuỳ chỉnh nào bên trong hàm bindEvents_ của trường đó, thì bạn cần huỷ đăng ký chúng bên trong hàm dispose.

Nếu bạn khởi động chế độ xem chính xác của trường (bằng cách thêm tất cả các phần tử DOM vào fieldGroup_), thì DOM của trường sẽ tự động được xử lý.

Xử lý giá trị

→ Để biết thông tin về giá trị của một trường so với văn bản của trường đó, hãy xem phần Cấu tạo của một trường.

Đơn đặt hàng xác thực

Sơ đồ quy trình mô tả thứ tự chạy trình xác thực

Triển khai trình xác thực lớp

Các trường chỉ nên chấp nhận một số giá trị nhất định. Ví dụ: các trường số chỉ nên chấp nhận số, các trường màu chỉ được chấp nhận màu sắc, v.v. Việc này được đảm bảo thông qua lớp và trình xác thực cục bộ. Trình xác thực lớp tuân theo các quy tắc tương tự như trình xác thực cục bộ, ngoại trừ việc trình xác thực này cũng chạy trong hàm khởi tạo và do đó, trình xác thực này không nên tham chiếu đến khối nguồn.

Để triển khai trình xác thực lớp của trường, hãy ghi đè hàm doClassValidation_.

doClassValidation_(newValue) {
  if (typeof newValue != 'string') {
    return null;
  }
  return newValue;
};

Xử lý các giá trị hợp lệ

Nếu giá trị được chuyển đến trường có setValue là hợp lệ, thì bạn sẽ nhận được lệnh gọi lại doValueUpdate_. Theo mặc định, hàm doValueUpdate_:

  • Thiết lập thuộc tính value_ thành newValue.
  • Đặt thuộc tính isDirty_ thành true.

Nếu chỉ cần lưu trữ giá trị này và không muốn thực hiện bất kỳ quá trình xử lý tuỳ chỉnh nào, thì bạn không cần ghi đè doValueUpdate_.

Ngược lại, nếu bạn muốn làm những việc như:

  • Bộ nhớ tuỳ chỉnh là newValue.
  • Thay đổi các thuộc tính khác dựa trên newValue.
  • Lưu để xem giá trị hiện tại có hợp lệ hay không.

Bạn sẽ cần ghi đè doValueUpdate_:

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

Xử lý giá trị không hợp lệ

Nếu giá trị được chuyển đến trường có setValue là không hợp lệ, bạn sẽ nhận được một lệnh gọi lại doValueInvalid_. Theo mặc định, hàm doValueInvalid_ không có tác dụng gì. Điều này có nghĩa là các giá trị không hợp lệ sẽ không xuất hiện theo mặc định. Điều này cũng có nghĩa là trường sẽ không được kết xuất lại vì thuộc tính isDirty_ sẽ không được đặt.

Nếu muốn hiển thị các giá trị không hợp lệ, bạn nên ghi đè doValueInvalid_. Trong hầu hết trường hợp, bạn nên đặt thuộc tính displayValue_ thành giá trị không hợp lệ, đặt isDirty_ thành trueghi đè kết xuất_ để màn hình hiển thị trên khối cập nhật dựa trên displayValue_ thay vì value_.

doValueInvalid_(newValue) {
  this.displayValue_ = newValue;
  this.isDirty_ = true;
  this.isValueValid_ = false;
}

Giá trị có nhiều phần

Khi trường của bạn chứa giá trị nhiều phần (ví dụ: danh sách, vectơ, đối tượng), bạn có thể muốn các phần đó được xử lý như các giá trị riêng lẻ.

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

Trong ví dụ trên, mỗi thuộc tính của newValue được xác thực riêng lẻ. Sau đó, ở cuối hàm doClassValidation_, nếu có bất kỳ thuộc tính riêng lẻ nào không hợp lệ, thì giá trị đó sẽ được lưu vào bộ nhớ đệm vào thuộc tính cacheValidatedValue_ trước khi trả về null (không hợp lệ). Việc lưu đối tượng vào bộ nhớ đệm bằng các thuộc tính được xác thực riêng cho phép hàm doValueInvalid_ xử lý riêng các thuộc tính đó, chỉ đơn giản bằng cách kiểm tra !this.cacheValidatedValue_.property, thay vì xác thực lại từng thuộc tính riêng lẻ.

Bạn cũng có thể sử dụng mẫu này để xác thực các giá trị gồm nhiều phần trong trình xác thực cục bộ nhưng hiện chưa có cách nào để thực thi mẫu này.

isDirty_

isDirty_ là một cờ dùng trong hàm setValue, cũng như các phần khác của trường, để cho biết liệu trường có cần được kết xuất lại hay không. Nếu giá trị hiển thị của trường đã thay đổi, thì bạn thường phải đặt isDirty_ thành true.

Văn bản

→ Để biết thông tin về vị trí sử dụng văn bản của một trường và sự khác biệt giữa văn bản đó với giá trị của trường đó, hãy xem phần Cấu tạo của một trường.

Nếu văn bản của trường khác với giá trị của trường, bạn nên ghi đè hàm getText để cung cấp văn bản chính xác.

getText() {
  let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
  if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
    text += ' hat';
  }
  return text;
}

Tạo trình chỉnh sửa

Nếu bạn xác định hàm showEditor_, thì Blockly sẽ tự động theo dõi các lượt nhấp và gọi showEditor_ vào thời điểm thích hợp. Bạn có thể hiển thị bất kỳ HTML nào trong trình chỉnh sửa bằng cách gói nó một trong hai div đặc biệt, có tên là DropDownDiv và WidgetDiv, nổi lên phía trên phần còn lại của giao diện người dùng của Blockly.

DropDownDiv dùng để cung cấp trình chỉnh sửa nằm trong một hộp được kết nối với một trường. Mã này sẽ tự động đặt mình ở gần trường trong khi vẫn nằm trong giới hạn có thể nhìn thấy. Công cụ chọn góc và công cụ chọn màu là những ví dụ điển hình của DropDownDiv.

Hình ảnh bộ chọn góc

WidgetDiv dùng để cung cấp cho các trình chỉnh sửa không nằm trong hộp. Các trường số sử dụng WidgetDiv để che trường có hộp nhập văn bản HTML. Mặc dù DropDownDiv xử lý việc điều chỉnh vị trí cho bạn, nhưng WidgetDiv thì không. Các phần tử cần được định vị theo cách thủ công. Hệ toạ độ nằm ở toạ độ pixel tương ứng với góc trên cùng bên trái của cửa sổ. Trình chỉnh sửa phương thức nhập văn bản là một ví dụ điển hình về WidgetDiv.

Hình ảnh trình chỉnh sửa phương thức nhập văn bản

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

Mã mẫu 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);
}

Dọn dẹp

Cả DropDownDiv và WidgetDiv đều xử lý đều huỷ bỏ các phần tử HTML của tiện ích, nhưng bạn cần loại bỏ theo cách thủ công mọi trình nghe sự kiện mà bạn đã áp dụng cho các phần tử đó.

widgetDispose_() {
  for (let i = this.editorListeners_.length, listener;
      listener = this.editorListeners_[i]; i--) {
    Blockly.browserEvents.unbind(listener);
    this.editorListeners_.pop();
  }
}

Hàm dispose được gọi trong ngữ cảnh null trên DropDownDiv. Trên WidgetDiv, phương thức này được gọi trong ngữ cảnh của WidgetDiv. Trong cả hai trường hợp, tốt nhất bạn nên sử dụng hàm bind khi truyền một hàm loại bỏ, như minh hoạ trong các ví dụ DropDownDivWidgetDiv ở trên.

→ Để biết thông tin về cách loại bỏ không dành riêng cho việc loại bỏ trình chỉnh sửa, hãy xem phần Loại bỏ.

Cập nhật chế độ hiển thị trên khối

Hàm render_ dùng để cập nhật chế độ hiển thị theo khối của trường cho khớp với giá trị nội bộ của trường.

Sau đây là một số ví dụ phổ biến:

  • Thay đổi văn bản (trình đơn thả xuống)
  • Thay đổi màu (màu sắc)

Mặc định

Hàm render_ mặc định đặt văn bản hiển thị thành kết quả của hàm getDisplayText_. Hàm getDisplayText_ trả về thuộc tính value_ của trường truyền đến một chuỗi, sau khi thuộc tính đó được cắt bớt để tuân theo độ dài văn bản tối đa.

Nếu bạn đang sử dụng chế độ hiển thị theo khối mặc định và hành vi văn bản mặc định phù hợp với trường của bạn, thì bạn không cần ghi đè render_.

Nếu hành vi văn bản mặc định phù hợp với trường của bạn, nhưng màn hình trên khối của trường có thêm các phần tử tĩnh, thì bạn có thể gọi hàm render_ mặc định, nhưng bạn vẫn cần ghi đè hàm đó để cập nhật kích thước của trường.

Nếu hành vi văn bản mặc định không phù hợp với trường của bạn hoặc màn hình hiển thị trên khối của trường có thêm các phần tử động, thì bạn cần tuỳ chỉnh hàm render_.

Sơ đồ quy trình mô tả cách đưa ra quyết định có ghi đè display_ hay không

Tuỳ chỉnh tính năng kết xuất

Nếu hành vi hiển thị mặc định không phù hợp với trường của bạn, thì bạn sẽ cần xác định hành vi hiển thị tuỳ chỉnh. Quá trình này có thể liên quan đến mọi việc từ thiết lập văn bản hiển thị tuỳ chỉnh, thay đổi các thành phần hình ảnh cho đến cập nhật màu nền.

Tất cả các thay đổi về thuộc tính DOM là hợp pháp, hai điều duy nhất cần nhớ là:

  1. Bạn nên xử lý việc tạo DOM trong quá trình khởi chạy vì cách này sẽ hiệu quả hơn.
  2. Bạn phải luôn cập nhật thuộc tính size_ cho phù hợp với kích thước của màn hình trên khối.
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_();
}

Đang cập nhật kích thước

Việc cập nhật thuộc tính size_ của một trường là rất quan trọng vì việc này sẽ thông báo cho mã kết xuất khối về cách định vị trường đó. Cách tốt nhất để tìm ra chính xác nội dung của size_ là bằng cách thử nghiệm.

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

Màu khối phù hợp

Nếu muốn các thành phần của trường khớp với màu của khối đính kèm, bạn nên ghi đè phương thức applyColour. Bạn sẽ muốn truy cập vào màu thông qua thuộc tính kiểu của khối.

applyColour() {
  const sourceBlock = this.sourceBlock_;
  if (sourceBlock.isShadow()) {
    this.arrow_.style.fill = sourceBlock.style.colourSecondary;
  } else {
    this.arrow_.style.fill = sourceBlock.style.colourPrimary;
  }
}

Đang cập nhật khả năng chỉnh sửa

Bạn có thể dùng hàm updateEditable để thay đổi cách hiển thị trường của mình tuỳ thuộc vào việc trường đó có thể chỉnh sửa hay không. Hàm mặc định tạo chế độ nền có/không có phản hồi di chuột (đường viền) nếu có/không thể chỉnh sửa. Màn hình trên khối không được thay đổi kích thước tuỳ thuộc vào khả năng chỉnh sửa, nhưng tất cả các thay đổi khác đều được cho phép.

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

Chuyển đổi tuần tự

Chuyển đổi tuần tự nghĩa là lưu trạng thái của trường để có thể tải lại vào không gian làm việc sau này.

Trạng thái không gian làm việc luôn bao gồm giá trị của trường, nhưng cũng có thể bao gồm các trạng thái khác, chẳng hạn như trạng thái giao diện người dùng của trường. Ví dụ: nếu trường của bạn là một bản đồ có thể thu phóng cho phép người dùng chọn các quốc gia, thì bạn cũng có thể chuyển đổi tuần tự mức thu phóng.

Nếu trường của bạn có thể chuyển đổi tuần tự, bạn phải đặt thuộc tính SERIALIZABLE thành true.

Blockly cung cấp 2 nhóm hook chuyển đổi tuần tự cho các trường. Một cặp hook hoạt động với hệ thống chuyển đổi tuần tự JSON mới, còn cặp hook còn lại hoạt động với hệ thống chuyển đổi tuần tự XML cũ.

saveStateloadState

saveStateloadState là các hook chuyển đổi tuần tự hoạt động với hệ thống chuyển đổi tuần tự JSON mới.

Trong một số trường hợp, bạn không cần cung cấp các mã này, vì phương thức triển khai mặc định sẽ hiệu quả. Nếu (1) trường của bạn là lớp con trực tiếp của lớp Blockly.Field cơ sở, (2) giá trị của bạn là loại chuyển đổi tuần tự JSON và (3) bạn chỉ cần chuyển đổi tuần tự giá trị, thì cách triển khai mặc định sẽ hoạt động tốt!

Nếu không, hàm saveState của bạn phải trả về một đối tượng/giá trị có thể chuyển đổi tuần tự JSON biểu thị trạng thái của trường. Và hàm loadState của bạn phải chấp nhận cùng một đối tượng/giá trị chuyển đổi tuần tự JSON và áp dụng đối tượng/giá trị đó cho trường.

saveState() {
  return {
    'country': this.getValue(),  // Value state
    'zoom': this.getZoomLevel(), // UI state
  };
}

loadState(state) {
  this.setValue(state['country']);
  this.setZoomLevel(state['zoom']);
}

Chuyển đổi tuần tự đầy đủ và sao lưu dữ liệu

saveState cũng nhận được một tham số doFullSerialization không bắt buộc. Tính năng này được sử dụng trong các trường thường tham chiếu đến trạng thái được chuyển đổi tuần tự bởi một trình chuyển đổi tuần tự khác (như mô hình dữ liệu sao lưu). Tham số báo hiệu rằng trạng thái được tham chiếu sẽ không có sẵn khi khối được giải tuần tự, vì vậy, trường sẽ thực hiện toàn bộ quá trình chuyển đổi tuần tự. Ví dụ: điều này đúng khi một khối riêng lẻ được chuyển đổi tuần tự hoặc khi một khối được sao chép và dán.

Hai trường hợp sử dụng phổ biến cho loại tệp này là:

  • Khi một khối riêng lẻ được tải vào không gian làm việc không tồn tại mô hình dữ liệu sao lưu, trường đó sẽ có đủ thông tin ở trạng thái riêng để tạo mô hình dữ liệu mới.
  • Khi một khối được sao chép và dán, trường này luôn tạo một mô hình dữ liệu sao lưu mới thay vì tham chiếu đến một mô hình hiện có.

Một trường sử dụng trường này là trường biến tích hợp. Thông thường, trình chuyển đổi tuần tự sẽ chuyển đổi tuần tự mã nhận dạng của biến đang tham chiếu, nhưng nếu doFullSerialization là đúng, thì hệ thống sẽ chuyển đổi tuần tự tất cả trạng thái của biến.

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

Trường biến thực hiện việc này để đảm bảo rằng nếu được tải vào một không gian làm việc mà biến không tồn tại, thì trường đó có thể tạo một biến mới để tham chiếu.

toXmlfromXml

toXmlfromXml là các hook chuyển đổi tuần tự hoạt động với hệ thống chuyển đổi tuần tự XML cũ. Chỉ sử dụng những hook này nếu cần (ví dụ: bạn đang làm việc trên một cơ sở mã cũ chưa di chuyển), nếu không, hãy sử dụng saveStateloadState.

Hàm toXml phải trả về một nút XML biểu thị trạng thái của trường. Và hàm fromXml của bạn phải chấp nhận cùng một nút XML và áp dụng nút đó cho trường.

toXml(fieldElement) {
  fieldElement.textContent = this.getValue();
  fieldElement.setAttribute('zoom', this.getZoomLevel());
  return fieldElement;
}

fromXml(fieldElement) {
  this.setValue(fieldElement.textContent);
  this.setZoomLevel(fieldElement.getAttribute('zoom'));
}

Thuộc tính có thể chỉnh sửa và chuyển đổi tuần tự

Thuộc tính EDITABLE xác định xem trường đó có cần giao diện người dùng để cho biết rằng trường đó có thể tương tác được hay không. Thuộc tính này mặc định là true.

Thuộc tính SERIALIZABLE xác định liệu trường có cần được chuyển đổi tuần tự hay không. Thuộc tính này mặc định là false. Nếu thuộc tính này là true, bạn có thể cần cung cấp các hàm chuyển đổi tuần tự và huỷ chuyển đổi tuần tự (xem phần Chuyển đổi tuần tự).

Tuỳ chỉnh con trỏ

Thuộc tính CURSOR xác định con trỏ mà người dùng nhìn thấy khi họ di chuột qua trường của bạn. Đây phải là một chuỗi con trỏ CSS hợp lệ. Thuộc tính này mặc định là con trỏ do .blocklyDraggable xác định, tức là con trỏ lấy.