Membuat blok prosedur kustom

Membuat blok prosedur kustom mengharuskan Anda:

  1. Instal plugin @blockly/block-shareable-procedures, seperti yang dijelaskan di halaman prosedur penggunaan.
  2. Gunakan sistem serialisasi JSON, seperti yang dijelaskan di halaman ringkasan.

Menambahkan model data ke ruang kerja

Blok pemanggil prosedur dan definisi prosedur mereferensikan model data pendukung yang menentukan tanda tangan prosedur (nama, parameter, dan tampilan). Hal ini memungkinkan fleksibilitas yang lebih baik dalam cara mendesain aplikasi Anda (misalnya, Anda dapat mengizinkan prosedur ditentukan di satu ruang kerja, dan dirujuk di ruang kerja lain).

Artinya, Anda harus menambahkan model data prosedur ke ruang kerja agar blok Anda dapat berfungsi. Ada banyak cara untuk melakukannya (misalnya, UI kustom).

@blockly/block-shareable-procedures dengan blok definisi prosedur secara dinamis membuat model data pendukungnya saat dibuat instance-nya ke ruang kerja. Untuk menerapkannya sendiri, buat model di init dan hapus di destroy.

import {ObservableProcedureModel} from '@blockly/block-shareable-procedures';

Blockly.Blocks['my_procedure_def'] = {
  init: function() {
    this.model = new ObservableProcedureModel('default name');
    this.workspace.getProcedureMap().add(model);
    // etc...
  }

  destroy: function() {
    // Optionally:
    // Destroy the model when the definition block is deleted.
    this.workpace.getProcedureMap().delete(model.getId());
  }
}

Menampilkan informasi tentang blok

Blok panggilan prosedur dan definisi prosedur Anda harus mengimplementasikan metode getProcedureModel, isProcedureDef, dan getVarModels. Ini adalah hook yang digunakan kode Blockly untuk mendapatkan informasi tentang blok prosedur Anda.

Blockly.Blocks['my_procedure_def'] = {
  getProcedureModel() {
    return this.model;
  },

  isProcedureDef() {
    return true;
  },

  getVarModels() {
    // If your procedure references variables
    // then you should return those models here.
    return [];
  },
};

Blockly.Blocks['my_procedure_call'] = {
  getProcedureModel() {
    return this.model;
  },

  isProcedureDef() {
    return false;
  },

  getVarModels() {
    // If your procedure references variables
    // then you should return those models here.
    return [];
  },
};

Memicu rendering ulang saat ada update

Blok panggilan prosedur dan definisi prosedur Anda perlu mengimplementasikan metode doProcedureUpdate. Ini adalah hook yang dipanggil model data untuk memberi tahu blok prosedur Anda agar merender ulang sendiri.

Blockly.Blocks['my_procedure_def'] = {
  doProcedureUpdate() {
    this.setFieldValue('NAME', this.model.getName());
    this.setFieldValue(
        'PARAMS',
        this.model.getParameters()
            .map((p) => p.getName())
            .join(','));
    this.setFieldValue(
        'RETURN', this.model.getReturnTypes().join(',');
  }
};

Blockly.Blocks['my_procedure_call'] = {
  doProcedureUpdate() {
    // Similar to the def block above...
  }
};

Menambahkan serialisasi kustom

Serialisasi untuk blok prosedur harus melakukan dua hal terpisah.

  1. Saat memuat dari JSON, blok Anda harus mengambil referensi ke model data pendukungnya, karena blok dan model diserialisasi secara terpisah.
  2. Saat menyalin dan menempelkan blok prosedur, blok tersebut perlu melakukan serialisasi seluruh status model prosedurnya, sehingga dapat direplikasi/diduplikasi.

Kedua hal ini ditangani melalui saveExtraState dan loadExtraState. Perlu diperhatikan lagi bahwa blok prosedur kustom hanya didukung saat menggunakan sistem serialisasi JSON, sehingga kita hanya perlu menentukan hook serialisasi JSON.

import {
    ObservableProcedureModel,
    ObservableParameterModel,
    isProcedureBlock
} from '@blockly/block-shareable-procedures';

Blockly.Blocks['my_procedure_def'] = {
  saveExtraState() {
    return {
      'procedureId': this.model.getId(),

      // These properties are only necessary for pasting.
      'name': this.model.getName(),
      'parameters': this.model.getParameters().map((p) => {
        return {name: p.getName(), p.getId()};
      }),
      'returnTypes': this.model.getReturnTypes(),
    };
  },

  loadExtraState(state) {
    const id = state['procedureId']
    const map = this.workspace.getProcedureMap();

    // Grab a reference to the existing procedure model.
    if (this.model.getId() != id && map.has(id) &&
        (this.isInsertionMarker || this.noBlockHasClaimedModel_(id))) {
      // Delete the existing model (created in init).
      this.workspace.getProcedureMap().delete(model.getId());
      // Grab a reference to the new model.
      this.model = this.workspace.getProcedureMap()
          .get(state['procedureId']);
      this.doProcedureUpdate();
      return;
    }

    // There is no existing procedure model (we are likely pasting), so
    // generate it from JSON.
    this.model
        .setName(state['name'])
        .setReturnTypes(state['returnTypes']);
    for (const [i, param] of state['parameters'].entries()) {
      this.model.insertParameter(
          i,
          new ObservableParameterModel(
              this.workspace, param['name'], param['id']));
    }
  },

  // We don't want to reference a model that some other procedure definition
  // is already referencing.
  noBlockHasClaimedModel_(procedureId) {
    const model =
      this.workspace.getProcedureMap().get(procedureId);
    return this.workspace.getAllBlocks(false).every(
      (block) =>
        !isProcedureBlock(block) ||
        !block.isProcedureDef() ||
        block.getProcedureModel() !== model);
  },
};

Blockly.Blocks['my_procedure_call'] = {
  saveExtraState() {
    return {
      'procedureId': this.model.getId(),
    };
  },

  loadExtraState(state) {
    // Delete our existing model (created in init).
    this.workspace.getProcedureMap().delete(model.getId());
    // Grab a reference to the new model.
    this.model = this.workspace.getProcedureMap()
        .get(state['procedureId']);
    if (this.model) this.doProcedureUpdate();
  },

  // Handle pasting after the procedure definition has been deleted.
  onchange(event) {
    if (event.type === Blockly.Events.BLOCK_CREATE &&
        event.blockId === this.id) {
      if(!this.model) { // Our procedure definition doesn't exist =(
        this.dispose();
      }
    }
  }
};

Jika ingin, ubah model prosedur/tanda tangan

Anda juga dapat menambahkan kemampuan bagi pengguna untuk mengubah model prosedur/tanda tangan. Memanggil metode insertParameter, deleteParameter, atau setReturnTypes akan otomatis memicu blok Anda untuk merender ulang (melalui doProcedureUpdate).

Opsi untuk membuat UI guna mengubah model prosedur mencakup penggunaan mutator (yang digunakan blok prosedur bawaan), kolom gambar dengan pengendali klik, sesuatu yang sepenuhnya berada di luar Blockly, dll.

Tambahkan blok ke toolbox

Kategori prosedur dinamis bawaan Blockly dikhususkan untuk blok prosedur bawaan Blockly. Jadi, agar dapat mengakses blok, Anda harus menentukan kategori dinamis kustom sendiri, dan menambahkannya ke toolbox.

const proceduresFlyoutCallback = function(workspace) {
  const blockList = [];
  blockList.push({
    'kind': 'block',
    'type': 'my_procedure_def',
  });
  for (const model of
        workspace.getProcedureMap().getProcedures()) {
    blockList.push({
      'kind': 'block',
      'type': 'my_procedure_call',
      'extraState': {
        'procedureId': model.getId(),
      },
    });
  }
  return blockList;
};

myWorkspace.registerToolboxCategoryCallback(
    'MY_PROCEDURES', proceduresFlyoutCallback);