ตัวแปรคือมิกซ์อินที่เพิ่มการจัดรูปแบบเพิ่มเติม (สถานะเพิ่มเติมที่บันทึกและโหลด) ลงในบล็อก เช่น บล็อก controls_if
และ list_create_with
ในตัวต้องมีการจัดรูปแบบเพิ่มเติมเพื่อให้บันทึกจํานวนอินพุตที่มีได้ นอกจากนี้ยังอาจเพิ่ม UI เพื่อให้ผู้ใช้เปลี่ยนรูปร่างของบล็อกได้
โปรดทราบว่าการเปลี่ยนรูปร่างของบล็อกไม่ได้หมายความว่าคุณต้องจัดเตรียมข้อมูลให้เป็นรูปแบบมาตรฐานเพิ่มเติม เช่น บล็อก math_number_property
จะเปลี่ยนรูปร่าง แต่การเปลี่ยนแปลงนั้นอิงตามช่องแบบเลื่อนลงซึ่งมีการจัดรูปแบบค่าไว้แล้ว ด้วยเหตุนี้ จึงใช้โปรแกรมตรวจสอบช่องได้โดยไม่ต้องใช้ตัวเปลี่ยนรูปแบบ
ดูข้อมูลเพิ่มเติมเกี่ยวกับกรณีที่ต้องใช้และไม่ต้องการใช้ตัวเปลี่ยนรูปแบบได้ที่หน้าการทำให้เป็นรูปแบบ
นอกจากนี้ ตัวแปรยังให้ UI ในตัวสำหรับผู้ใช้เพื่อเปลี่ยนรูปร่างของบล็อกด้วยหากคุณระบุเมธอดที่ไม่บังคับ
ฮุกการทำให้ให้เป็นอนุกรม
ตัวแปรมีฮุกการจัดรูปแบบ 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 แบบเก่า ใช้ฮุกเหล่านี้เฉพาะในกรณีที่จำเป็นเท่านั้น (เช่น คุณกําลังทํางานกับฐานโค้ดเก่าที่ยังไม่ได้ย้ายข้อมูล) มิเช่นนั้นให้ใช้ 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 Hook
หากคุณระบุฟังก์ชันบางอย่างเป็นส่วนหนึ่งของตัวเปลี่ยนรูปแบบ Blockly จะเพิ่ม UI "ตัวเปลี่ยนรูปแบบ" เริ่มต้นลงในบล็อก
คุณไม่จำเป็นต้องใช้ UI นี้หากต้องการเพิ่มการแปลงเป็นอนุกรมเพิ่มเติม คุณอาจใช้ UI ที่กําหนดเอง เช่น ปลั๊กอิน blocks-plus-minus ให้มา หรือจะใช้ UI เลยก็ได้
เขียนและแยกวิเคราะห์
UI เริ่มต้นใช้ฟังก์ชัน compose
และ decompose
decompose
"ระเบิด" บล็อกออกเป็นบล็อกย่อยขนาดเล็กซึ่งสามารถย้ายไปรอบๆ เพิ่ม และลบได้ ฟังก์ชันนี้ควรแสดงผล "บล็อกด้านบน" ซึ่งเป็นบล็อกหลักในพื้นที่ทํางานของตัวแปรที่บล็อกย่อยเชื่อมต่ออยู่
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
ฟังก์ชันจะเชื่อมต่อรายการย่อยของบล็อกหลักอีกครั้งอย่างถูกต้องเมื่อมีการ reorganize บล็อกย่อย
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();
}
},
กำลังลงทะเบียน
ตัวแปรเปลี่ยนเป็นเพียงมิกซ์อินประเภทพิเศษ จึงต้องลงทะเบียนด้วยก่อนจึงจะใช้ในคําจํากัดความ 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
: สตริงที่จะเชื่อมโยงกับตัวเปลี่ยนรูปแบบเพื่อให้คุณใช้ใน JSON ได้mixinObj
: ออบเจ็กต์ที่มีเมธอดต่างๆ ของการกลายพันธุ์ เช่นsaveExtraState
และloadExtraState
opt_helperFn
: ฟังก์ชันตัวช่วยที่ไม่บังคับซึ่งจะทำงานในบล็อกหลังจากผสานมิกซ์อินแล้วopt_blockList
: อาร์เรย์ประเภทบล็อก (สตริง) ที่ไม่บังคับซึ่งจะเพิ่มลงในเมนูแบบเลื่อนขึ้นใน UI ของตัวแปรเริ่มต้น หากมีการกําหนดเมธอด UI ด้วย
โปรดทราบว่าบล็อกแต่ละประเภทมีตัวเปลี่ยนรูปแบบได้เพียงรายการเดียว ซึ่งแตกต่างจากชิ้นงาน
{
//...
"mutator": "controls_if_mutator"
}
ฟังก์ชันตัวช่วย
นอกเหนือจากมิกซ์อินแล้ว ตัวเปลี่ยนรูปแบบยังอาจลงทะเบียนฟังก์ชันตัวช่วยด้วย ฟังก์ชันนี้จะทำงานกับแต่ละบล็อกของประเภทที่ระบุหลังจากสร้างและเพิ่ม mixinObj แล้ว ซึ่งสามารถใช้เพื่อเพิ่มทริกเกอร์หรือเอฟเฟกต์เพิ่มเติมลงในการกลายพันธุ์
เช่น คุณอาจเพิ่มตัวช่วยลงในบล็อกที่มีลักษณะเป็นลิสต์ซึ่งกำหนดจำนวนรายการเริ่มต้น ดังนี้
var helper = function() {
this.itemCount_ = 5;
this.updateShape();
}