Tạo trường tuỳ chỉnh

Trước khi tạo một 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 một giao diện người dùng mới cho một loại giá trị hiện có, thì có thể bạn cần tạo một loại trường mới.

Để tạo một 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ý quá trình khởi chạy giao diện người dùng trên 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 sẽ được xử lý cho bạn).
  5. Triển khai tính năng xử lý giá trị.
  6. Thêm một bản trình bày bằng văn bản về giá trị của trường để hỗ trợ tiếp cận.
  7. Thêm các chức năng bổ sung, chẳng hạn như:
  8. Định cấu hình các khía cạnh khác của trường, chẳng hạn như:

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

Để xem 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 dựng của trường tuỳ chỉnh được gọi trong quá trình khởi tạo khối nguồn, bất kể khối nguồn được xác định bằng 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 tạo.

Mã mẫu sau đây sẽ tạo một trường tuỳ chỉnh có tên là 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à một trình xác thực cục bộ. Giá trị này 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 vượt qua quá trình xác thự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ớ truyền một 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. Tìm hiểu thêm về trình xác thực trong tài liệu Trình xác thực.

Cấu trúc

Logic bên trong hàm khởi tạo của bạn phải tuân theo quy trình sau:

  1. Gọi hàm khởi tạo siêu 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 tạo đú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ể được 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 có thể chuyển đổi tuần tự và các trường có thể chỉnh sửa theo mặc định, vì vậy, bạn nên đặt thuộc tính này thành true, trừ phi bạn biết rằng thuộc tính này không được chuyển đổi tuần tự.
  3. Không bắt buộc: Áp dụng chế độ tuỳ chỉnh bổ sung (ví dụ: Trường nhãn cho phép truyền một lớp css, sau đó lớp này sẽ được á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). Blockly duy trì một bản đồ 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 tạo.

Gọi Blockly.fieldRegistry.register để thêm loại trường vào bản đồ này, truyền vào lớp trường dưới dạng đố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 phải huỷ tham chiếu mọi tham chiếu đến mã thông báo bản địa hoá bằng cách sử dụng replaceMessageReferences, sau đó truyề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 một giá trị. Khởi tạo là nơi DOM được tạo, mô hình được tạo (nếu trường có một mô hình) và các sự kiện được liên kết.

Hiển thị trên khối

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

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

Hàm initView mặc định tạo một phần tử rect có màu sáng và một phần tử text. Nếu bạn muốn trường của mình có cả hai, cộng với một số tính năng bổ sung, hãy gọi hàm initView của lớp mẹ trước khi thêm phần còn lại của các phần tử DOM. Nếu muốn trường của mình có một trong hai phần tử này, nhưng không phải cả hai, bạn có thể sử dụng 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ụ: Text Input), thì quá trình tạo DOM sẽ được xử lý cho bạn. Nếu không, bạn sẽ cần phải ghi đè hàm initView để tạo các phần tử DOM mà bạn sẽ cần trong quá trình kết xuất trường sau này.

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

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

Các yêu cầu đối với chế độ hiển thị trên 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 kích thướ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 về cách tuỳ chỉnh và cập nhật màn hình hiển thị trên khối.

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

Nếu muốn thêm biểu tượng 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ể nối 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ú thích và sự kiện mousedown (được dùng để hiện 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 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;
      }
  );
}

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

Thả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 sẽ cần huỷ đăng ký các trình nghe đó bên trong hàm dispose.

Nếu bạn đã khởi chạy chế độ xem của trường một cách chính xác (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 bị loại bỏ.

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 Kết cấu của một trường.

Thứ tự xác thực

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

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

Các trường chỉ được chấp nhận một số giá trị nhất định. Ví dụ: các trường số chỉ được chấp nhận số, các trường màu chỉ được chấp nhận màu, v.v. Điều này được đảm bảo thông qua trình xác thực lớp và 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 được chạy trong hàm khởi tạo và do đó, trình xác thực này không được 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 truyền đến một trường có setValue là hợp lệ, bạn sẽ nhận được lệnh gọi lại doValueUpdate_. Theo mặc định, hàm doValueUpdate_:

  • Đặt 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ị và không muốn thực hiện bất kỳ thao tác xử lý tuỳ chỉnh nào, bạn không cần ghi đè doValueUpdate_.

Nếu không, nếu bạn muốn thực hiện các thao tác như:

  • Bộ nhớ tuỳ chỉnh 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ý các giá trị không hợp lệ

Nếu giá trị được truyền đến trường có setValue không hợp lệ, bạn sẽ nhận được lệnh gọi lại doValueInvalid_. Theo mặc định, hàm doValueInvalid_ không làm gì cả. Điều này có nghĩa là theo mặc định, các giá trị không hợp lệ sẽ không xuất hiện. Đ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 các 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 đè render_ cho 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 một giá trị gồm 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 đều được xác thực riêng lẻ. Sau đó, ở cuối hàm doClassValidation_, nếu có thuộc tính riêng lẻ nào không hợp lệ, giá trị sẽ được lưu vào bộ nhớ đệm cho thuộc tính cacheValidatedValue_ trước khi trả về null (không hợp lệ). Việc lưu vào bộ nhớ đệm đối tượng bằng các thuộc tính được xác thực riêng lẻ cho phép hàm doValueInvalid_ xử lý các thuộc tính này riêng biệt, chỉ bằng cách thực hiện một thao tác 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 tại, không có cách nào để thực thi mẫu này.

isDirty_

isDirty_ là một cờ đượ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, isDirty_ thường phải được đặt 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à điểm khác biệt giữa văn bản đó với giá trị của trường, hãy xem phần Kết cấu 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 người chỉnh sửa

Nếu bạn xác định hàm showEditor_, Blockly sẽ tự động lắng nghe 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 bao bọc HTML đó bằng một trong hai div đặc biệt, được gọi là DropDownDiv và WidgetDiv. Các div này sẽ nổi phía trên phần còn lại của giao diện người dùng Blockly.

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

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

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

Hình ảnh của trình chỉnh sửa 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ý việc huỷ các phần tử HTML của tiện ích, nhưng bạn cần huỷ bỏ mọi trình nghe sự kiện mà bạn đã áp dụng cho các phần tử đó theo cách thủ công.

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 là bạn nên sử dụng hàm bind khi truyền một hàm dispose, như minh hoạ trong các ví dụ DropDownDivWidgetDiv ở trên.

→ Để biết thông tin về việc xử lý không dành riêng cho việc xử lý trình chỉnh sửa, hãy xem phần Xử lý.

Cập nhật màn hình hiển thị trên khối

Hàm render_ được dùng để cập nhật màn hình hiển thị trên khối của trường cho phù hợ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 sắc (màu)

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 được truyền thành một chuỗi, sau khi thuộc tính đó bị cắt bớt để tuân thủ độ dài tối đa của văn bản.

Nếu đang sử dụng chế độ hiển thị mặc định trên khối 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 hiển thị trên khối của trường có thêm các phần tử tĩnh, bạn có thể gọi hàm render_ mặc định, nhưng bạn vẫn cần ghi đè hàm này để 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 hoạt động cho 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, bạn sẽ cần tuỳ chỉnh hàm render_.

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

Tuỳ chỉnh hoạt động hiển thị

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

Mọi thay đổi về thuộc tính DOM đều hợp lệ, bạn chỉ cần nhớ 2 điều sau:

  1. Bạn nên xử lý việc tạo DOM trong quá trình khởi chạy, vì việc này hiệu quả hơn.
  2. Bạn phải luôn cập nhật thuộc tính size_ để khớp với kích thước của quảng cáo hiển thị 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ì thuộc tính này cho biết mã kết xuất khối cách định vị trường. Cách tốt nhất để xác định chính xác size_ đó là 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;
}

Ghép các khối màu

Nếu muốn các phần tử trong trường của bạn khớp với màu của khối mà chúng được đí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 trường của bạn xuất hiện, tuỳ thuộc vào việc trường đó có thể chỉnh sửa hay không. Hàm mặc định giúp đảm bảo nền có/không có phản hồi di chuột (đường viền) nếu có/không chỉnh sửa được. Màn hình hiển thị 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 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ự

Tuần tự hoá là quá trình 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 của 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 trạng thái khác, chẳng hạn như trạng thái của 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 quốc gia, thì bạn cũng có thể chuyển đổi cấp độ thu phóng thành chuỗi.

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

Blockly cung cấp hai 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 và cặp 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 tuần tự hoá hoạt động với hệ thống tuần tự hoá JSON mới.

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

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, đại diện cho 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ị có thể chuyển đổi tuần tự thành 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']);
}

Toàn bộ quá trình chuyển đổi tuần tự và dữ liệu sao lưu

saveState cũng nhận được một tham số không bắt buộc là doFullSerialization. Trường này được dùng bởi các trường thường tham chiếu đến trạng thái được chuyển đổi tuần tự bằng một trình chuyển đổi tuần tự khác (chẳng hạn như các mô hình dữ liệu sao lưu). Tham số này báo hiệu rằng trạng thái được tham chiếu sẽ không có sẵn khi khối được chuyển đổi tuần tự, vì vậy, trường này sẽ tự thực hiện tất cả 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.

Sau đây là 2 trường hợp sử dụng phổ biến cho việc này:

  • Khi một khối riêng lẻ được tải vào một không gian làm việc mà mô hình dữ liệu hỗ trợ không tồn tại, trường này có đủ thông tin trong trạng thái riêng để tạo một 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 hỗ trợ mới thay vì tham chiếu đến một mô hình hiện có.

Một trường sử dụng biến này là trường biến được tạo sẵn. Thông thường, nó sẽ chuyển đổi mã nhận dạng của biến mà nó đang tham chiếu thành dữ liệu nối tiếp, nhưng nếu doFullSerialization có giá trị true, thì nó sẽ chuyển đổi tất cả trạng thái của biến đó thành dữ liệu nối tiếp.

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 của nó không tồn tại, thì trường biến có thể tạo một biến mới để tham chiếu.

toXmlfromXml

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

Hàm toXml của bạn phải trả về một nút XML đại diện cho trạng thái của trường. 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ó nên có giao diện người dùng để cho biết rằng trường đó có thể tương tác hay không. Giá trị mặc định là true.

Thuộc tính SERIALIZABLE xác định xem trường có được chuyển đổi tuần tự hay không. Giá trị 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à giải tuần tự (xem Chuyển đổi tuần tự).

Tuỳ chỉnh bằng CSS

Bạn có thể tuỳ chỉnh trường bằng CSS. Trong phương thức initView, hãy thêm một lớp tuỳ chỉnh vào fieldGroup_ của trường, sau đó tham chiếu lớp này trong CSS.

Ví dụ: để sử dụng một con trỏ khác:

initView() {
  ...

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

Tuỳ chỉnh con trỏ

Theo mặc định, các lớp mở rộng FieldInput sẽ sử dụng con trỏ text khi người dùng di chuột qua trường, các trường đang được kéo sẽ sử dụng con trỏ grabbing và tất cả các trường khác sẽ sử dụng con trỏ default. Nếu bạn muốn sử dụng một con trỏ khác, hãy đặt con trỏ đó bằng CSS.