Mutator

Mutator adalah mixin yang menambahkan serialisasi tambahan (status tambahan yang disimpan dan dimuat) ke blok. Misalnya, blok controls_if dan list_create_with bawaan memerlukan serialisasi tambahan agar dapat menyimpan jumlah input yang dimilikinya. Hal ini juga dapat menambahkan UI sehingga pengguna dapat mengubah bentuk blok.

Tiga mutasi blok buat daftar: tanpa input, tiga input, dan lima input.

Dua mutasi blok if/do: if-do dan if-do-else-if-do-else.

Perhatikan bahwa mengubah bentuk blok tidak selalu berarti Anda memerlukan serialisasi tambahan. Misalnya, blok math_number_property berubah bentuk, tetapi hal itu dilakukan berdasarkan kolom dropdown, yang nilainya sudah diserialisasi. Oleh karena itu, kolom ini hanya dapat menggunakan validator kolom, dan tidak memerlukan mutator.

Blok `math_number_property` dengan menu dropdown yang disetel ke `even`. Blok ini memiliki
input nilai tunggal. Blok `math_number_property`
dengan dropdown yang ditetapkan ke `dapat dibagi dengan`. Blok ini memiliki dua input
nilai.

Lihat halaman serialisasi untuk mengetahui informasi selengkapnya tentang kapan Anda memerlukan mutator dan kapan tidak.

Mutator juga menyediakan UI bawaan bagi pengguna untuk mengubah bentuk blok jika Anda menyediakan beberapa metode opsional.

Hook serialisasi

Mutator memiliki dua pasang hook serialisasi yang digunakan. Satu pasang hook berfungsi dengan sistem serialisasi JSON baru, dan pasang hook lainnya berfungsi dengan sistem serialisasi XML lama. Anda harus memberikan setidaknya satu pasangan ini.

saveExtraState dan loadExtraState

saveExtraState dan loadExtraState adalah hook serialisasi yang berfungsi dengan sistem serialisasi JSON baru. saveExtraState menampilkan nilai yang dapat diserialisasi JSON yang merepresentasikan status tambahan blok, dan loadExtraState menerima nilai yang dapat diserialisasi JSON yang sama, dan menerapkannya ke blok.

// 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 yang dihasilkan akan terlihat seperti:

{
  "type": "lists_create_with",
  "extraState": {
    "itemCount": 3 // or whatever the count is
  }
}

Tanpa status

Jika blok Anda dalam status default saat diserialisasi, metode saveExtraState dapat menampilkan null untuk menunjukkannya. Jika metode saveExtraState Anda menampilkan null, tidak ada properti extraState yang ditambahkan ke JSON. Hal ini akan menjaga ukuran file penyimpanan Anda tetap kecil.

Serialisasi penuh dan data pendukung

saveExtraState juga menerima parameter doFullSerialization opsional. Hal ini digunakan oleh blok yang mereferensikan status yang diserialisasi oleh serializer yang berbeda (seperti model data pendukung). Parameter menandakan bahwa status yang dirujuk tidak akan tersedia saat blok dideserialisasi, sehingga blok harus menserialisasi semua status pendukungnya sendiri. Misalnya, hal ini benar saat blok individual diserialisasi, atau saat blok disalin-tempel.

Dua kasus penggunaan umum untuk hal ini adalah:

  • Saat blok individual dimuat ke ruang kerja yang tidak memiliki model data pendukung, blok tersebut memiliki informasi yang cukup dalam statusnya sendiri untuk membuat model data baru.
  • Saat diblokir, salin dan tempel, selalu buat model data pendukung baru, bukan merujuk ke model yang sudah ada.

Beberapa blok yang menggunakan ini adalah blok @blockly/block-shareable-procedures. Biasanya, mereka melakukan serialisasi referensi ke model data pendukung, yang menyimpan statusnya. Namun, jika parameter doFullSerialization benar, maka mereka akan melakukan serialisasi semua statusnya. Blok prosedur yang dapat dibagikan menggunakan ini untuk memastikan bahwa saat disalin dan ditempel, blok tersebut membuat model data pendukung baru, bukan merujuk ke model yang ada.

mutationToDom dan domToMutation

mutationToDom dan domToMutation adalah hook serialisasi yang berfungsi dengan sistem serialisasi XML lama. Gunakan hook ini hanya jika Anda harus melakukannya (misalnya, Anda sedang mengerjakan codebase lama yang belum dimigrasikan), jika tidak, gunakan saveExtraState dan loadExtraState.

mutationToDom menampilkan node XML yang merepresentasikan status tambahan blok, dan domToMutation menerima node XML yang sama dan menerapkan status ke blok.

// 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 yang dihasilkan akan terlihat seperti:

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

Jika fungsi mutationToDom Anda menampilkan null, tidak ada elemen tambahan yang akan ditambahkan ke XML.

Hook UI

Jika Anda menyediakan fungsi tertentu sebagai bagian dari mutator, Blockly akan menambahkan UI "mutator" default ke blok Anda.

Blok if-do dengan balon mutatornya terbuka. Hal ini memungkinkan pengguna menambahkan klausa else-if dan else ke blok if-do.

Anda tidak harus menggunakan UI ini jika ingin menambahkan serialisasi tambahan. Anda dapat menggunakan UI kustom, seperti yang disediakan oleh plugin blocks-plus-minus, atau tidak menggunakan UI sama sekali.

menyusun dan menguraikan

UI default mengandalkan fungsi compose dan decompose.

decompose "meledakkan" blok menjadi sub-blok yang lebih kecil yang dapat dipindahkan, ditambahkan, dan dihapus. Fungsi ini harus menampilkan "blok teratas" yang merupakan blok utama di ruang kerja mutator yang terhubung ke sub-blok.

compose kemudian menafsirkan konfigurasi sub-blok dan menggunakannya untuk memodifikasi blok utama. Fungsi ini harus menerima "blok atas" yang ditampilkan oleh decompose sebagai parameter.

Perhatikan bahwa fungsi ini "dicampur" ke dalam blok yang "dimutasi" sehingga this dapat digunakan untuk merujuk ke blok tersebut.

// 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

Secara opsional, Anda juga dapat menentukan fungsi saveConnections yang berfungsi dengan UI default. Fungsi ini memberi Anda kesempatan untuk mengaitkan turunan dari blok utama (yang ada di ruang kerja utama) dengan sub-blok yang ada di ruang kerja mutator Anda. Kemudian, Anda dapat menggunakan data ini untuk memastikan fungsi compose terhubung kembali dengan benar ke turunan blok utama saat sub-blok Anda disusun ulang.

saveConnections harus menerima "blok atas" yang ditampilkan oleh fungsi decompose Anda sebagai parameter. Jika fungsi saveConnections ditentukan, Blockly akan memanggilnya sebelum memanggil 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();
  }
},

Mendaftarkan

Mutator hanyalah jenis mixin khusus, sehingga juga harus didaftarkan sebelum Anda dapat menggunakannya dalam definisi JSON jenis blok.

// 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: String untuk dikaitkan dengan mutator sehingga Anda dapat menggunakannya di JSON.
  • mixinObj: Objek yang berisi berbagai metode mutasi. Misalnya, saveExtraState dan loadExtraState.
  • opt_helperFn: Fungsi helper opsional yang akan berjalan di blok setelah mixin dicampur.
  • opt_blockList: Array jenis blok opsional (sebagai string) yang akan ditambahkan ke flyout di UI pengubah default, jika metode UI juga ditentukan.

Perhatikan bahwa tidak seperti ekstensi, setiap jenis blok hanya dapat memiliki satu mutator.

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

Fungsi bantuan

Bersama dengan mixin, mutator dapat mendaftarkan fungsi helper. Fungsi ini dijalankan pada setiap blok jenis tertentu setelah dibuat dan mixinObj ditambahkan. Hal ini dapat digunakan untuk menambahkan pemicu atau efek tambahan ke mutasi.

Misalnya, Anda dapat menambahkan helper ke blok seperti daftar yang menetapkan jumlah awal item:

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