Ekstensi dan Mutator

Ekstensi adalah fungsi yang berjalan di setiap blok dari jenis tertentu saat blok dibuat. Hal ini sering menambahkan beberapa konfigurasi atau perilaku kustom ke blok.

Mutator adalah jenis ekstensi khusus yang menambahkan serialisasi kustom, dan terkadang UI, ke blok.

Ekstensi

Ekstensi adalah fungsi yang berjalan di setiap blok dari jenis tertentu saat blok dibuat. Modul ini dapat menambahkan konfigurasi kustom (mis. menyetel tooltip blok) atau perilaku kustom (mis. menambahkan pemroses peristiwa ke blok).

// 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;
      });
    });

Ekstensi harus "terdaftar" agar dapat dikaitkan dengan kunci string. Kemudian, Anda dapat menetapkan kunci string ini ke properti extensions dari definisi JSON jenis blok Anda untuk menerapkan ekstensi ke blok.

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

Anda juga dapat menambahkan beberapa ekstensi sekaligus. Perhatikan bahwa properti extensions harus berupa array, meskipun Anda hanya menerapkan satu ekstensi.

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

Mixin

Blockly juga menyediakan metode praktis untuk situasi ketika Anda ingin menambahkan beberapa properti/fungsi helper ke sebuah blok, tetapi tidak menjalankannya secara langsung. Dengan begitu, Anda dapat mendaftarkan objek mixin yang berisi semua properti/metode tambahan. Objek mixin kemudian digabungkan dalam fungsi yang menerapkan mixin setiap kali instance jenis blok yang ditentukan dibuat.

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

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

Kunci string yang terkait dengan mixin dapat direferensikan di JSON seperti ekstensi lainnya.

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

Mutator

Mutator adalah jenis ekstensi khusus yang menambahkan serialisasi ekstra (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 dimiliki.

Perhatikan bahwa mengubah bentuk blok tidak selalu berarti Anda memerlukan serialisasi tambahan. Misalnya, blok math_number_property mengubah bentuk, tetapi melakukannya berdasarkan kolom dropdown, yang nilainya sudah diserialisasi. Dengan demikian, fungsi ini hanya dapat menggunakan validator kolom, dan tidak memerlukan mutator.

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

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 pasangan hook lainnya berfungsi dengan sistem serialisasi XML lama. Anda harus menyediakan setidaknya salah satu dari pasangan ini.

simpanExtraState dan loadExtraState

saveExtraState dan loadExtraState adalah hook serialisasi yang berfungsi dengan sistem serialisasi JSON baru. saveExtraState menampilkan nilai JSON yang dapat diserialisasi yang mewakili status tambahan blok, dan loadExtraState menerima nilai serialisable JSON yang sama tersebut, 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 ini:

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

Jika blok Anda berada dalam status default saat diserialisasi, metode saveExtraState dapat menampilkan null untuk menunjukkan hal ini. Jika metode saveExtraState menampilkan null, tidak ada properti extraState yang akan ditambahkan ke JSON. Hal ini membuat ukuran file yang Anda simpan tetap kecil.

Serialisasi dan pencadangan data lengkap

saveExtraState juga menerima parameter doFullSerialization opsional. API ini digunakan oleh blok status referensi yang diserialisasi oleh serializer berbeda (seperti model data pendukung). Parameter ini memberi sinyal bahwa status yang dirujuk tidak akan tersedia saat blok dideserialisasi, sehingga blok harus melakukan serialisasi semua status cadangan itu sendiri. Misalnya, hal ini benar jika satu blok diserialisasi, atau saat blok disalin-ditempel.

Dua kasus penggunaan umum untuk hal ini adalah:

  • Saat blok individual dimuat ke ruang kerja yang tidak memiliki model data pendukung, blok tersebut memiliki cukup informasi dalam statusnya sendiri untuk membuat model data baru.
  • Jika suatu blok disalin-tempel, blok tersebut akan selalu membuat model data pendukung baru, bukan mereferensikan model yang sudah ada.

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

mutationToDom dan domToMutation

mutationToDom dan domToMutation adalah hook serialisasi yang berfungsi dengan sistem serialisasi XML lama. Hanya gunakan hook ini jika Anda perlu (misalnya, Anda sedang mengerjakan code base lama yang belum dimigrasikan). Jika tidak, gunakan saveExtraState dan loadExtraState.

mutationToDom menampilkan node XML yang mewakili status tambahan blok, dan domToMutation menerima node XML yang sama dan menerapkan status pada 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 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.

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

menyusun dan mengurai

UI default bergantung pada fungsi compose dan decompose.

decompose "menguraikan" blok menjadi sub-blok yang lebih kecil yang dapat dipindahkan, ditambahkan, dan dihapus. Fungsi ini akan menampilkan "top block" yang merupakan blok utama di ruang kerja mutator tempat sub-blok terhubung.

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

Perhatikan bahwa fungsi ini "dicampur" ke 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 blok utama (yang ada di ruang kerja utama) dengan sub-blok yang ada di ruang kerja mutator Anda. Selanjutnya, Anda dapat menggunakan data ini untuk memastikan fungsi compose menghubungkan kembali turunan blok utama dengan benar saat sub-blok diatur ulang.

saveConnections harus menerima "blok atas" yang ditampilkan oleh fungsi decompose 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 ekstensi khusus. Jadi, Mutator juga harus didaftarkan sebelum Anda dapat menggunakannya dalam definisi JSON jenis blok Anda.

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

Perlu diketahui bahwa tidak seperti ekstensi, setiap jenis blok mungkin hanya memiliki satu mutator.

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

Fungsi helper

Bersamaan dengan mixin, mutator dapat mendaftarkan fungsi bantuan. Fungsi ini dijalankan pada setiap blok dari jenis yang ditentukan setelah dibuat dan mixinObj ditambahkan. 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();
}