Mutator là một mixin bổ sung thêm quá trình chuyển đổi tuần tự (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_if
và list_create_with
tích hợp cần thêm quá trình chuyển đổi tuần tự để có thể lưu số lượng đầu vào mà chúng có. Thao tác này cũng có thể thêm một giao diện người dùng để người dùng có thể thay đổi hình dạng của khối.
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 thêm quá trình chuyển đổi tuần tự. Ví dụ: khối math_number_property
thay đổi hình dạng, nhưng khối này thực hiện việc đó dựa trên một trường thả xuống, giá trị của trường này đã được chuyển đổi tuần tự. Do đó, nó chỉ có thể sử dụng một trình xác thực trường và không cần một trình đột biến.
Hãy xem trang tuần tự hoá để biết thêm thông tin về thời điểm bạn cần và không cần trình biến đổi.
Các đối tượng đột biến cũng cung cấp một giao diện người dùng tích hợp để 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 tuần tự hoá
Mutator có 2 cặp hook tuần tự hoá mà chúng hoạt động cù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ũ. Bạn phải cung cấp ít nhất một trong các cặp này.
saveExtraState và loadExtraState
saveExtraState
và loadExtraState
là các hook tuần tự hoá hoạt động với hệ thống tuần tự hoá JSON mới. saveExtraState
trả về một giá trị có thể chuyển đổi tuần tự JSON, đại diện cho trạng thái bổ sung của khối và loadExtraState
chấp nhận cùng một giá trị có thể 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 kết quả 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ếu khối ở 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ì không có thuộc tính extraState
nào được thêm vào JSON. Điều này giúp giảm kích thước tệp lưu.
Toàn bộ quá trình chuyển đổi tuần tự và dữ liệu sao lưu
saveExtraState
cũng nhận được một tham số doFullSerialization
không bắt buộc. Thành phần này được dùng bởi các khối tham chiếu 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, khối sẽ tự 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.
Sau đây là 2 trường hợp sử dụng phổ biến cho tính năng 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, khối đó có đủ thông tin trong trạng thái của riêng mình để tạo một mô hình dữ liệu mới.
- Khi một khối được sao chép và dán, khối đó 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 số khối sử dụng khối này là khối @blockly/block-shareable-procedures. Thông thường, các thành phần này sẽ chuyển đổi tuần tự một tham chiếu đến mô hình dữ liệu hỗ trợ, mô hình này lưu trữ trạng thái của chúng.
Nhưng nếu tham số doFullSerialization
là true, thì chúng sẽ chuyển đổi tất cả trạng thái của mình thành chuỗi. Các khối quy trình có thể chia sẻ sử dụng phương thức này để đảm bảo rằng khi được sao chép và dán, các khối này sẽ 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ó.
mutationToDom và domToMutation
mutationToDom
và domToMutation
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 saveExtraState
và loadExtraState
.
mutationToDom
trả về một nút XML đại diện cho trạng thái bổ sung của khối và domToMutation
chấp nhận cùng một nút XML đó rồi á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 kết quả 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ử nào khác được thêm vào XML.
UI Hooks
Nếu bạn cung cấp một số chức năng trong mutator, Blockly sẽ thêm giao diện người dùng "mutator" mặc định vào khối của bạn.
Bạn không bắt buộc phải sử dụng giao diện người dùng này nếu muốn thêm quá trình 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, hoặc bạn có thể không sử dụng giao diện người dùng nào cả!
phân tích và tổng hợp
Giao diện người dùng mặc định dựa vào các hàm compose
và decompose
.
decompose
"phân tách" khối thành các khối phụ nhỏ hơn có thể di chuyển xung quanh, thêm 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 chương trình sửa đổi mà các khối phụ kết nối đến.
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ề làm tham số.
Xin lưu ý rằng các hàm này được "trộn" vào khối đang được "đột biến" để có thể 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 phép bạn liên kết các phần tử con của khối chính (nằm trên không gian làm việc chính) với các khối phụ nằm trong không gian làm việc của đối tượng sửa đổi. Sau đó, bạn có thể dùng dữ liệu này để đảm bảo hàm compose
của bạn kết nối lại đúng cách các phần tử 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ề làm 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ý
Mutator chỉ là một loại mixin đặc biệt, vì vậy, bạn cũng phải đăng ký chúng trước khi có thể sử dụ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 đối tượng sửa đổ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 đột biến. Ví dụ:saveExtraState
vàloadExtraState
.opt_helperFn
: Một hàm trợ giúp không bắt buộc sẽ chạy trên khối sau khi mixin được trộn vào.opt_blockList
: Một mảng không bắt buộc gồm các loại khối (dưới dạng chuỗi) sẽ được thêm vào trình đơn bật lên trong giao diện người dùng trình biến đổi 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ư các tiện ích, mỗi loại khối chỉ có thể có một đối tượng sửa đổi.
{
//...
"mutator": "controls_if_mutator"
}
Hàm trợ giúp
Cùng với mixin, một mutator có thể đăng ký một hàm trợ giúp. Hàm này được chạy trên mỗi khối thuộc loại đã cho sau khi khối đó được tạo và mixinObj
được thêm. Bạn có thể dùng tính năng này để thêm các điều kiện kích hoạt hoặc hiệu ứng bổ sung vào một đột biến.
Ví dụ: bạn có thể thêm một đối tượng hỗ trợ 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();
}