Tạo khối quy trình tuỳ chỉnh

Việc tạo khối quy trình tuỳ chỉnh yêu cầu bạn phải:

  1. Cài đặt trình bổ trợ @blockly/block-shareable-procedures, như mô tả trên trang sử dụng quy trình.
  2. Sử dụng hệ thống chuyển đổi tuần tự JSON, như giải thích trên trang tổng quan.

Thêm mô hình dữ liệu vào không gian làm việc

Cả phương thức định nghĩa quy trình và phương thức gọi quy trình đều tham chiếu đến một mô hình dữ liệu sao lưu giúp xác định chữ ký của quy trình (tên, tham số và dữ liệu trả về). Điều này giúp bạn linh hoạt hơn trong cách thiết kế ứng dụng (ví dụ: bạn có thể cho phép các quy trình được xác định trong một không gian làm việc và tham chiếu trong không gian làm việc khác).

Điều này có nghĩa là bạn sẽ cần thêm mô hình dữ liệu quy trình vào không gian làm việc để các khối của bạn sử dụng. Có nhiều cách để bạn có thể làm việc này (ví dụ: giao diện người dùng tuỳ chỉnh).

@blockly/block-shareable-procedures bằng cách yêu cầu khối định nghĩa quy trình tự động tạo các mô hình dữ liệu dự phòng khi chúng được tạo thực thể vào không gian làm việc. Để tự triển khai việc này, bạn sẽ tạo mô hình trong init rồi xoá mô hình đó trong 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());
  }
}

Trả về thông tin về các khối

Các khối lệnh gọi quy trình và định nghĩa quy trình cần triển khai các phương thức getProcedureModel, isProcedureDefgetVarModels. Đây là các hook mà mã Blockly sử dụng để lấy thông tin về các khối quy trình của bạn.

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 [];
  },
};

Kích hoạt quá trình kết xuất lại trên bản cập nhật

Các khối lệnh gọi quy trình và định nghĩa quy trình cần triển khai phương thức doProcedureUpdate. Đây là điểm nối của lệnh gọi mô hình dữ liệu để yêu cầu các khối quy trình của bạn tự kết xuất lại.

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...
  }
};

Thêm chuỗi tuần tự tuỳ chỉnh

Việc chuyển đổi tuần tự các khối quy trình phải thực hiện hai việc riêng biệt.

  1. Khi tải từ JSON, các khối của bạn sẽ cần lấy tệp tham chiếu đến mô hình dữ liệu sao lưu, vì các khối và mô hình này được chuyển đổi tuần tự riêng biệt.
  2. Khi sao chép và dán một khối quy trình, khối đó sẽ cần chuyển đổi tuần tự toàn bộ trạng thái của mô hình quy trình để có thể sao chép/trùng lặp quy trình đó.

Cả hai việc này đều được xử lý thông qua saveExtraStateloadExtraState. Xin lưu ý rằng các khối quy trình tuỳ chỉnh chỉ được hỗ trợ khi sử dụng hệ thống chuyển đổi tuần tự JSON. Vì vậy, chúng ta chỉ cần xác định hook chuyển đổi tuần tự 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();
      }
    }
  }
};

Sửa đổi mô hình/chữ ký quy trình (không bắt buộc)

Bạn cũng có thể thêm khả năng cho người dùng sửa đổi mô hình/chữ ký quy trình. Việc gọi các phương thức insertParameter, deleteParameter hoặc setReturnTypes sẽ tự động kích hoạt các khối của bạn để hiển thị lại (thông qua doProcedureUpdate).

Các tuỳ chọn để tạo giao diện người dùng để sửa đổi mô hình quy trình bao gồm sử dụng trình biến đổi (mà quy trình tích hợp chặn sử dụng), các trường hình ảnh có trình xử lý lượt nhấp, một nội dung hoàn toàn nằm ngoài Blockly, v.v.

Thêm khối vào hộp công cụ

Danh mục quy trình động tích hợp sẵn của Blockly là danh mục dành riêng cho các khối quy trình tích hợp sẵn của Blockly. Vì vậy, để có thể truy cập vào các quy tắc chặn của mình, bạn cần xác định danh mục động tuỳ chỉnh của riêng mình và thêm danh mục đó vào hộp công cụ.

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