Tiện ích và yếu tố đột biến

Tiện ích là các hàm chạy trên mỗi khối của một loại nhất định khi khối đó được tạo. Các lệnh này thường thêm một số cấu hình hoặc hành vi tuỳ chỉnh vào khối.

Trình biến đổi là một loại tiện ích đặc biệt có chức năng thêm quá trình chuyển đổi tuần tự tuỳ chỉnh và đôi khi là giao diện người dùng vào một khối.

Phần mở rộng

Tiện ích là các hàm chạy trên mỗi khối của một loại nhất định khi khối đó được tạo. Chúng có thể thêm cấu hình tuỳ chỉnh (ví dụ: đặt chú giải công cụ của khối) hoặc hành vi tuỳ chỉnh (ví dụ: thêm trình nghe sự kiện vào khối).

// This extension sets the block's tooltip to be a function which displays
// the parent block's tooltip (if it exists).
Blockly.Extensions.register(
    'parent_tooltip_extension',
    function() { // this refers to the block that the extension is being run on
      var thisBlock = this;
      this.setTooltip(function() {
        var parent = thisBlock.getParent();
        return (parent && parent.getInputsInline() && parent.tooltip) ||
            Blockly.Msg.MATH_NUMBER_TOOLTIP;
      });
    });

Các tiện ích phải được "đăng ký" để có thể liên kết với một khoá chuỗi. Sau đó, bạn có thể chỉ định khoá chuỗi này cho thuộc tính extensions của định nghĩa JSON của loại khối để áp dụng phần mở rộng cho khối.

{
 //...,
 "extensions": ["parent_tooltip_extension",]
}

Bạn cũng có thể thêm nhiều tiện ích cùng một lúc. Lưu ý rằng thuộc tính extensions phải là một mảng, ngay cả khi bạn chỉ áp dụng một phần mở rộng.

{
  //...,
  "extensions": ["parent_tooltip_extension", "break_warning_extension"],
}

Danh sách kết hợp nhạc

Blockly cũng cung cấp một phương thức thuận tiện cho trường hợp bạn muốn thêm một số thuộc tính/hàm trợ giúp vào một khối, nhưng không chạy các thuộc tính/hàm trợ giúp đó ngay lập tức. Tính năng này hoạt động bằng cách cho phép bạn đăng ký một đối tượng mixin chứa tất cả thuộc tính/phương thức bổ sung. Sau đó, đối tượng Mixin được gói trong một hàm áp dụng sự kết hợp mỗi khi một thực thể của loại khối nhất định được tạo.

Blockly.Extensions.registerMixin('my_mixin', {
  someProperty: 'a cool value',

  someMethod: function() {
    // Do something cool!
  }
))`

Các khoá chuỗi liên kết với mixin có thể được tham chiếu trong JSON giống như mọi tiện ích khác.

{
 //...,
 "extensions": ["my_mixin"],
}

Bộ đột biến

Trình biến đổi là một loại tiện ích đặc biệt có chức năng thêm quá trình chuyển đổi tuần tự bổ sung (trạng thái bổ sung được lưu và tải) vào một khối. Ví dụ: các khối controls_iflist_create_with tích hợp sẵn cần thêm quy trình chuyển đổi tuần tự để có thể lưu số lượng dữ liệu đầu vào đã có.

Xin lưu ý rằng việc thay đổi hình dạng của khối không nhất thiết có nghĩa là bạn cần chuyển đổi tuần tự thêm. Ví dụ: khối math_number_property thay đổi hình dạng, nhưng có thể điều này dựa trên trường thả xuống có giá trị đã được chuyển đổi tuần tự. Do đó, bạn chỉ cần sử dụng trình xác thực trường mà không cần trình biến đổi.

Hãy xem trang chuyển đổi tuần tự để biết thêm thông tin về thời điểm bạn cần và khi nào không cần biến thể.

Các bộ biến đổi cũng cung cấp một giao diện người dùng tích hợp sẵn để người dùng thay đổi hình dạng của các khối nếu bạn cung cấp một số phương thức không bắt buộc.

Hook chuyển đổi tuần tự

Trình tạo đột biến có 2 cặp hook chuyển đổi tuần tự tương ứ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ũ. Bạn phải cung cấp ít nhất một trong các cặp này.

saveExtraState và loadExtraState

saveExtraStateloadExtraState 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. saveExtraState trả về một giá trị chuyển đổi tuần tự JSON biểu thị trạng thái bổ sung của khối, và loadExtraState chấp nhận cùng một giá trị chuyển đổi tuần tự JSON đó và áp dụng giá trị đó cho khối.

// These are the serialization hooks for the lists_create_with block.
saveExtraState: function() {
  return {
    'itemCount': this.itemCount_,
  };
},

loadExtraState: function(state) {
  this.itemCount_ = state['itemCount'];
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

JSON thu được sẽ có dạng như sau:

{
  "type": "lists_create_with",
  "extraState": {
    "itemCount": 3 // or whatever the count is
  }
}
Không có tiểu bang nào

Nếu khối của bạn ở trạng thái mặc định khi được chuyển đổi tuần tự, thì phương thức saveExtraState có thể trả về null để cho biết điều này. Nếu phương thức saveExtraState của bạn trả về null thì sẽ không có thuộc tính extraState nào được thêm vào JSON. Việc này giúp tệp lưu trữ có kích thước nhỏ.

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

saveExtraState 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 khối 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, khối phải chuyển đổi tuần tự tất cả trạng thái sao lưu. 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 một không gian làm việc không tồn tại mô hình dữ liệu sao lưu, khối đó sẽ có đủ thông tin ở trạng thái riêng để tạo một mô hình dữ liệu mới.
  • Khi được sao chép và dán, một khối 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 số khối sử dụng quy tắc chặn này là @blockly/block-shareable-procedures. Thông thường, chúng sẽ chuyển đổi tuần tự tham chiếu đến một mô hình dữ liệu sao lưu, giúp lưu trữ trạng thái của chúng. Nhưng nếu tham số doFullSerialization là đúng, thì chúng sẽ chuyển đổi tuần tự tất cả trạng thái. Các khối quy trình có thể chia sẻ sử dụng quy trình này để đảm bảo rằng khi được sao chép và dán, chúng sẽ 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ó.

mutationToDom và domToMutation

mutationToDomdomToMutation 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 được di chuyển), nếu không, hãy sử dụng saveExtraStateloadExtraState.

mutationToDom trả về một nút XML biểu thị trạng thái bổ sung của khối, đồng thời domToMutation chấp nhận nút XML đó và áp dụng trạng thái cho khối.

// These are the old XML serialization hooks for the lists_create_with block.
mutationToDom: function() {
  // You *must* create a <mutation></mutation> element.
  // This element can have children.
  var container = Blockly.utils.xml.createElement('mutation');
  container.setAttribute('items', this.itemCount_);
  return container;
},

domToMutation: function(xmlElement) {
  this.itemCount_ = parseInt(xmlElement.getAttribute('items'), 10);
  // This is a helper function which adds or removes inputs from the block.
  this.updateShape_();
},

XML thu được sẽ có dạng như sau:

<block type="lists_create_with">
  <mutation items="3"></mutation>
</block>

Nếu hàm mutationToDom của bạn trả về giá trị rỗng, thì sẽ không có phần tử bổ sung nào được thêm vào XML.

Hook giao diện người dùng

Nếu bạn cung cấp một số hàm nhất định như một phần của đối tượng biến đổi, thì Blockly sẽ thêm giao diện người dùng "trình biến đổi" mặc định vào khối của bạn.

Bạn không phải sử dụng giao diện người dùng này nếu muốn thêm chuyển đổi tuần tự bổ sung. Bạn có thể sử dụng giao diện người dùng tuỳ chỉnh, chẳng hạn như trình bổ trợ blocks-plus-minus cung cấp, hoặc bạn có thể không sử dụng giao diện người dùng nào!

soạn và phân huỷ

Giao diện người dùng mặc định dựa vào các hàm composedecompose.

decompose "nổ" khối này thành các khối phụ nhỏ hơn có thể di chuyển, thêm vào và xoá. Hàm này sẽ trả về một "khối trên cùng" là khối chính trong không gian làm việc của trình biến đổi mà các khối phụ kết nối.

Sau đó, compose sẽ diễn giải cấu hình của các khối phụ và sử dụng chúng để sửa đổi khối chính. Hàm này phải chấp nhận "khối trên cùng" do decompose trả về dưới dạng tham số.

Lưu ý rằng các hàm này được "kết hợp" với khối bị "biến đổi" nên bạn có thể sử dụng this để tham chiếu đến khối đó.

// These are the decompose and compose functions for the lists_create_with block.
decompose: function(workspace) {
  // This is a special sub-block that only gets created in the mutator UI.
  // It acts as our "top block"
  var topBlock = workspace.newBlock('lists_create_with_container');
  topBlock.initSvg();

  // Then we add one sub-block for each item in the list.
  var connection = topBlock.getInput('STACK').connection;
  for (var i = 0; i < this.itemCount_; i++) {
    var itemBlock = workspace.newBlock('lists_create_with_item');
    itemBlock.initSvg();
    connection.connect(itemBlock.previousConnection);
    connection = itemBlock.nextConnection;
  }

  // And finally we have to return the top-block.
  return topBlock;
},

// The container block is the top-block returned by decompose.
compose: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we collect up all of the connections of on our main block that are
  // referenced by our sub-blocks.
  // This relates to the saveConnections hook (explained below).
  var connections = [];
  while (itemBlock && !itemBlock.isInsertionMarker()) {  // Ignore insertion markers!
    connections.push(itemBlock.valueConnection_);
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }

  // Then we disconnect any children where the sub-block associated with that
  // child has been deleted/removed from the stack.
  for (var i = 0; i < this.itemCount_; i++) {
    var connection = this.getInput('ADD' + i).connection.targetConnection;
    if (connection && connections.indexOf(connection) == -1) {
      connection.disconnect();
    }
  }

  // Then we update the shape of our block (removing or adding iputs as necessary).
  // `this` refers to the main block.
  this.itemCount_ = connections.length;
  this.updateShape_();

  // And finally we reconnect any child blocks.
  for (var i = 0; i < this.itemCount_; i++) {
    connections[i].reconnect(this, 'ADD' + i);
  }
},

saveConnections

Nếu muốn, bạn cũng có thể xác định một hàm saveConnections hoạt động với giao diện người dùng mặc định. Hàm này cho bạn cơ hội liên kết các khối con của khối chính (tồn tại trên không gian làm việc chính) với các khối phụ tồn tại trong không gian làm việc của trình biến đổi. Sau đó, bạn có thể sử dụng dữ liệu này để đảm bảo compose kết nối lại đúng cách các thành phần con của khối chính khi các khối phụ được sắp xếp lại.

saveConnections phải chấp nhận "khối trên cùng" do hàm decompose trả về dưới dạng tham số. Nếu hàm saveConnections được xác định, Blockly sẽ gọi hàm này trước khi gọi compose.

saveConnections: function(topBlock) {
  // First we get the first sub-block (which represents an input on our main block).
  var itemBlock = topBlock.getInputTargetBlock('STACK');

  // Then we go through and assign references to connections on our main block
  // (input.connection.targetConnection) to properties on our sub blocks
  // (itemBlock.valueConnection_).
  var i = 0;
  while (itemBlock) {
    // `this` refers to the main block (which is being "mutated").
    var input = this.getInput('ADD' + i);
    // This is the important line of this function!
    itemBlock.valueConnection_ = input && input.connection.targetConnection;
    i++;
    itemBlock = itemBlock.nextConnection &&
        itemBlock.nextConnection.targetBlock();
  }
},

Đang đăng ký

Trình đột biến chỉ là một loại tiện ích đặc biệt, vì vậy, bạn cũng phải đăng ký trước khi có thể sử dụng chúng trong định nghĩa JSON của loại khối.

// Function signature.
Blockly.Extensions.registerMutator(name, mixinObj, opt_helperFn, opt_blockList);

// Example call.
Blockly.Extensions.registerMutator(
    'controls_if_mutator',
    { /* mutator methods */ },
    undefined,
    ['controls_if_elseif', 'controls_if_else']);
  • name: Một chuỗi để liên kết với biến đổi để bạn có thể sử dụng chuỗi đó trong JSON.
  • mixinObj: Một đối tượng chứa nhiều phương thức biến đổi. Ví dụ: saveExtraStateloadExtraState.
  • opt_helperFn: Một hàm trợ giúp (không bắt buộc) sẽ chạy trên khối sau khi kết hợp trộn.
  • opt_blockList: Một mảng các loại khối không bắt buộc (dưới dạng chuỗi) sẽ được thêm vào cửa sổ bay trong giao diện người dùng biến thể mặc định, nếu các phương thức giao diện người dùng cũng được xác định.

Xin lưu ý rằng không giống như tiện ích, mỗi loại khối chỉ có thể có một biến thể.

{
  //...
  "mutator": "controls_if_mutator"
}

Chức năng trợ giúp

Cùng với mixin, một biến thể (muter) có thể đăng ký một hàm trợ giúp. Hàm này chạy trên từng khối của loại đã cho sau khi tạo và thêm mixinObj. Bạn có thể dùng kỹ thuật này để thêm điều kiện kích hoạt hoặc hiệu ứng bổ sung vào một điểm đột biến.

Ví dụ: bạn có thể thêm một trình trợ giúp vào khối giống như danh sách để đặt số lượng mục ban đầu:

var helper = function() {
  this.itemCount_ = 5;
  this.updateShape();
}