Mutator คือ Mixin ที่เพิ่มการซีเรียลไลซ์เพิ่มเติม (สถานะเพิ่มเติมที่ได้รับการบันทึก
และโหลด) ลงในบล็อก เช่น บล็อก controls_if
และ list_create_with
ในตัวต้องมีการซีเรียลไลซ์เพิ่มเติมเพื่อให้บันทึกจำนวนอินพุตที่มีได้ นอกจากนี้ยังอาจเพิ่ม UI เพื่อให้ผู้ใช้เปลี่ยนรูปร่างของบล็อกได้ด้วย
โปรดทราบว่าการเปลี่ยนรูปร่างของบล็อกไม่ได้หมายความว่าคุณต้องมีการซีเรียลไลซ์เพิ่มเติม
เช่น บล็อก math_number_property
เปลี่ยนรูปร่าง
แต่จะเปลี่ยนตามช่องแบบเลื่อนลงซึ่งค่าจะได้รับการ
ซีเรียลไลซ์อยู่แล้ว ดังนั้นจึงใช้ได้แค่เครื่องมือตรวจสอบฟิลด์ และไม่
ต้องใช้ Mutator
ดูข้อมูลเพิ่มเติมเกี่ยวกับเวลาที่คุณต้องใช้ Mutator และเวลาที่คุณไม่ต้องใช้ได้ที่หน้าการซีเรียลไลซ์
นอกจากนี้ Mutator ยังมี UI ในตัวเพื่อให้ผู้ใช้เปลี่ยนรูปร่างของบล็อกได้หาก คุณระบุเมธอดที่ไม่บังคับบางอย่าง
ฮุกการเรียงอันดับ
Mutator มีฮุกการซีเรียลไลซ์ 2 คู่ที่ใช้ทำงานด้วย โดยฮุกคู่หนึ่ง จะทำงานร่วมกับระบบการซีเรียลไลซ์ JSON ใหม่ และอีกคู่จะทำงานร่วมกับ ระบบการซีเรียลไลซ์ XML เก่า คุณต้องระบุคู่ข้อมูลเหล่านี้อย่างน้อย 1 คู่
saveExtraState และ loadExtraState
saveExtraState
และ loadExtraState
คือฮุกการซีเรียลไลซ์ที่ใช้ได้กับ
ระบบการซีเรียลไลซ์ JSON ใหม่ saveExtraState
จะแสดงค่าที่สามารถแปลงเป็น JSON ได้
ซึ่งแสดงถึงสถานะพิเศษของบล็อก และ loadExtraState
ยอมรับค่าที่สามารถแปลงเป็น JSON เดียวกันนั้น แล้วนำไปใช้กับบล็อก
// 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 ที่ได้จะมีลักษณะดังนี้
{
"type": "lists_create_with",
"extraState": {
"itemCount": 3 // or whatever the count is
}
}
ไม่มีสถานะ
หากบล็อกอยู่ในสถานะเริ่มต้นเมื่อมีการซีเรียลไลซ์ เมธอด saveExtraState
จะแสดงผล null
เพื่อระบุสถานะนี้ได้ หาก
saveExtraState
เมธอดแสดงผลเป็น null
ระบบจะไม่เพิ่มพร็อพเพอร์ตี้ extraState
ลงใน
JSON ซึ่งจะช่วยให้ไฟล์บันทึกมีขนาดเล็ก
การแปลงข้อมูลเป็นอนุกรมและการสำรองข้อมูลทั้งหมด
saveExtraState
ยังรับพารามิเตอร์ doFullSerialization
ที่ไม่บังคับด้วย บล็อกที่อ้างอิงสถานะที่ซีเรียลไลซ์โดยซีเรียลไลเซอร์อื่น (เช่น โมเดลข้อมูลสำรอง) จะใช้
สิ่งนี้ พารามิเตอร์จะส่งสัญญาณว่า
สถานะที่อ้างอิงจะใช้ไม่ได้เมื่อมีการยกเลิกการซีเรียลไลซ์บล็อก ดังนั้น
บล็อกควรซีเรียลไลซ์สถานะการสำรองข้อมูลทั้งหมดด้วยตัวเอง เช่น จะเป็น
จริงเมื่อมีการทำให้บล็อกแต่ละรายการเป็นอนุกรม หรือเมื่อมีการคัดลอกและวางบล็อก
กรณีการใช้งานที่พบบ่อย 2 กรณีมีดังนี้
- เมื่อโหลดบล็อกแต่ละรายการลงในพื้นที่ทํางานที่ไม่มีโมเดลข้อมูล สำรอง บล็อกจะมีข้อมูลเพียงพอในสถานะของตัวเองเพื่อสร้างโมเดลข้อมูลใหม่
- เมื่อคัดลอกและวางบล็อก ระบบจะสร้างโมเดลข้อมูลสำรองใหม่เสมอแทนที่จะอ้างอิงโมเดลที่มีอยู่
บล็อกบางรายการที่ใช้ฟีเจอร์นี้คือบล็อก @blockly/block-shareable-procedures โดยปกติแล้ว
จะแปลงการอ้างอิงไปยังโมเดลข้อมูลสำรองซึ่งจัดเก็บสถานะของโมเดล
แต่หากพารามิเตอร์ doFullSerialization
เป็นจริง ระบบจะทำให้สถานะทั้งหมดเป็นอนุกรม
บล็อกขั้นตอนที่แชร์ได้จะใช้สิ่งนี้เพื่อให้แน่ใจว่าเมื่อมีการคัดลอกและวาง จะมีการสร้างโมเดลข้อมูลสำรองใหม่แทนการอ้างอิงโมเดลที่มีอยู่
mutationToDom และ domToMutation
mutationToDom
และ domToMutation
คือฮุกการแปลงเป็นอนุกรมที่ใช้ได้กับ
ระบบการแปลงเป็นอนุกรม XML แบบเดิม ใช้ Hook เหล่านี้เฉพาะในกรณีที่จำเป็น (เช่น คุณกำลังทำงานกับโค้ดเบสเก่าที่ยังไม่ได้ย้ายข้อมูล) ไม่เช่นนั้น ให้ใช้ saveExtraState
และ loadExtraState
mutationToDom
จะแสดงโหนด XML ที่แสดงถึงสถานะเพิ่มเติมของ
บล็อก และ domToMutation
จะยอมรับโหนด XML เดียวกันนั้นและใช้สถานะกับ
บล็อก
// 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 ที่ได้จะมีลักษณะดังนี้
<block type="lists_create_with">
<mutation items="3"></mutation>
</block>
หากฟังก์ชัน mutationToDom
แสดงผลเป็น Null ระบบจะไม่เพิ่มองค์ประกอบใดๆ ลงใน XML
UI Hooks
หากคุณระบุฟังก์ชันบางอย่างเป็นส่วนหนึ่งของ Mutator Blockly จะเพิ่ม UI "Mutator" เริ่มต้นลงในบล็อก
คุณไม่จำเป็นต้องใช้ UI นี้หากต้องการเพิ่มการซีเรียลไลซ์เพิ่มเติม คุณอาจใช้ UI ที่กำหนดเอง เช่น ปลั๊กอิน blocks-plus-minus หรืออาจไม่ใช้ UI เลยก็ได้
การรวมและการแยก
UI เริ่มต้นจะใช้ฟังก์ชัน compose
และ decompose
decompose
"ขยาย" บล็อกให้เป็นบล็อกย่อยที่เล็กลงซึ่งสามารถย้าย
ไปมา เพิ่ม และลบได้ ฟังก์ชันนี้ควรแสดงผล "บล็อกบนสุด" ซึ่งเป็นบล็อกหลักในพื้นที่ทำงานของ Mutator ที่บล็อกย่อยเชื่อมต่ออยู่
compose
จากนั้นจะตีความการกำหนดค่าของบล็อกย่อยและใช้เพื่อ
แก้ไขบล็อกหลัก ฟังก์ชันนี้ควรยอมรับ "บล็อกบนสุด" ซึ่งdecompose
ส่งคืนเป็นพารามิเตอร์
โปรดทราบว่าฟังก์ชันเหล่านี้จะ "ผสม" ลงในบล็อกที่ "กลายพันธุ์" ดังนั้นจึงใช้ this
เพื่ออ้างอิงถึงบล็อกนั้นได้
// 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
นอกจากนี้ คุณยังกำหนดฟังก์ชัน saveConnections
ที่ใช้ได้กับ UI เริ่มต้นได้ด้วย ฟังก์ชันนี้ช่วยให้คุณมีโอกาสเชื่อมโยงบล็อกย่อยของบล็อกหลัก (ซึ่งอยู่ในพื้นที่ทำงานหลัก) กับบล็อกย่อยที่อยู่ในพื้นที่ทำงานของตัวแก้ไข จากนั้นคุณสามารถใช้ข้อมูลนี้เพื่อให้แน่ใจว่าcompose
ฟังก์ชันจะเชื่อมต่อบล็อกย่อยของบล็อกหลักอีกครั้งอย่างถูกต้องเมื่อมีการจัดระเบียบบล็อกย่อย
ใหม่
saveConnections
ควรยอมรับ "บล็อกบนสุด" ที่ฟังก์ชัน decompose
ส่งคืนเป็นพารามิเตอร์ หากมีการกำหนดฟังก์ชัน saveConnections
Blockly
จะเรียกใช้ฟังก์ชันนี้ก่อนเรียกใช้ 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();
}
},
กำลังลงทะเบียน
Mutator เป็นเพียงมิกซ์อินประเภทพิเศษ ดังนั้นคุณจึงต้องลงทะเบียนก่อนจึงจะใช้ในคำจำกัดความ JSON ของประเภทบล็อกได้
// 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
: สตริงที่จะเชื่อมโยงกับ Mutator เพื่อให้คุณใช้ใน JSON ได้mixinObj
: ออบเจ็กต์ที่มีเมธอดการเปลี่ยนแปลงต่างๆ เช่นsaveExtraState
และloadExtraState
opt_helperFn
: ฟังก์ชันช่วยที่ไม่บังคับซึ่งจะ ทำงานในบล็อกหลังจากที่รวมมิกซ์อินแล้วopt_blockList
: อาร์เรย์ประเภทบล็อกที่ไม่บังคับ (เป็นสตริง) ซึ่งจะ เพิ่มลงในเมนูแบบลอยใน UI ของตัวเปลี่ยนเริ่มต้น หากมีการกำหนดเมธอด UI ด้วย
โปรดทราบว่าบล็อกแต่ละประเภทจะมีตัวแปรได้เพียง 1 ตัวเท่านั้น ซึ่งต่างจากส่วนขยาย
{
//...
"mutator": "controls_if_mutator"
}
ฟังก์ชันตัวช่วย
นอกเหนือจากมิกซ์อินแล้ว มิวเทเตอร์ยังอาจลงทะเบียนฟังก์ชันตัวช่วยได้ด้วย ฟังก์ชันนี้จะทำงานในแต่ละบล็อกของประเภทที่ระบุหลังจากที่สร้างและเพิ่ม mixinObj
แล้ว ซึ่งสามารถใช้เพื่อเพิ่มทริกเกอร์หรือเอฟเฟกต์เพิ่มเติมในการเปลี่ยนแปลงได้
เช่น คุณอาจเพิ่มตัวช่วยลงในบล็อกที่คล้ายรายการซึ่งกำหนด จำนวนรายการเริ่มต้นได้
var helper = function() {
this.itemCount_ = 5;
this.updateShape();
}