Benutzerdefinierte Prozedurblöcke erstellen

Für das Erstellen von benutzerdefinierten Prozedurblöcken sind folgende Schritte erforderlich:

  1. Installieren Sie das Plug-in @blockly/block-shareable-procedures, wie auf der Seite Verfahren verwenden beschrieben.
  2. Verwenden Sie das JSON-Serialisierungssystem, wie auf der Übersichtsseite erläutert.

Datenmodelle zum Arbeitsbereich hinzufügen

Sowohl Prozedurdefinitions- als auch Prozeduraufruf-Blöcke verweisen auf ein unterstützendes Datenmodell, das die Signatur der Prozedur definiert (Name, Parameter und Rückgabe). Dies ermöglicht mehr Flexibilität bei der Entwicklung Ihrer Anwendung (z.B. können Prozeduren in einem Arbeitsbereich definiert und in einem anderen referenziert werden).

Das bedeutet, dass Sie die Prozedurdatenmodelle dem Arbeitsbereich hinzufügen müssen, damit Ihre Blöcke funktionieren. Dafür gibt es viele Möglichkeiten, z.B. mit benutzerdefinierten UIs.

Die @blockly/block-shareable-procedures erfüllt dies, indem ihre zugrunde liegenden Datenmodelle dynamisch mit Prozedurdefinitionsblöcken erstellt werden, wenn sie im Arbeitsbereich instanziiert werden. Wenn Sie dies selbst implementieren möchten, erstellen Sie das Modell in init und löschen es in 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.

    // Insertion markers reference the model of the original block.
    if (this.isInsertionMarker()) return;
    this.workpace.getProcedureMap().delete(model.getId());
  }
}

Informationen zu den Blöcken zurückgeben

Ihre Prozedurdefinition und Prozeduraufrufblöcke müssen die Methoden getProcedureModel, isProcedureDef und getVarModels implementieren. Dies sind die Hooks, die Blockly-Code verwendet, um Informationen zu Ihren Prozedurblöcken abzurufen.

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

Re-Rendering bei Updates auslösen

Ihre Prozedurdefinition und Prozeduraufrufblöcke müssen die Methode doProcedureUpdate implementieren. Dies ist der Hook, den die Datenmodelle aufrufen, um Ihre Prozedurblöcke anzuweisen, sich selbst neu zu rendern.

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

Benutzerdefinierte Serialisierung hinzufügen

Die Serialisierung von Prozedurblöcken muss zwei separate Vorgänge ausführen.

  1. Beim Laden aus JSON müssen die Blöcke einen Verweis auf ihr zugrunde liegendes Datenmodell abrufen, da die Blöcke und Modelle separat serialisiert werden.
  2. Beim Kopieren und Einfügen eines Prozedurblocks muss der Block den gesamten Status seines Prozedurmodells serialisieren, damit er repliziert werden kann.

Beide Vorgänge werden über saveExtraState und loadExtraState abgewickelt. Beachten Sie, dass benutzerdefinierte Prozedurblöcke nur bei Verwendung des JSON-Serialisierungssystems unterstützt werden. Daher müssen wir nur JSON-Serialisierungs-Hooks definieren.

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

Blockly.Blocks['my_procedure_def'] = {
  // When doFullSerialization is true, we should serialize the full state of
  // the model.
  saveExtraState(doFullSerialization) {
    const state = Object.create(null);
    state['procedureId']: this.model.getId();

    if (doFullSerialization) {
      state['name'] = this.model.getName();
      state['parameters'] = this.model.getParameters().map((p) => {
        return {name: p.getName(), p.getId()};
      });
      state['returnTypes'] = this.model.getReturnTypes();

      // Flag for deserialization.
      state['createNewModel'] = true;
    }

    return state;
  },

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

    if (map.has(id) && !state['createNewModel']) {
      // Delete the existing model (created in init).
      map.delete(this.model.getId());
      // Grab a reference to the model we're supposed to reference.
      this.model = map.get(id);
      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']));
    }
    this.doProcedureUpdate();
  },
};

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

Prozedurmodell ändern (optional)

Sie können den Nutzenden auch die Möglichkeit hinzufügen, das Prozedurmodell zu ändern. Durch den Aufruf der Methoden insertParameter, deleteParameter oder setReturnTypes werden die Blöcke automatisch über doProcedureUpdate neu gerendert.

Zum Erstellen von UIs zum Ändern des Prozedurmodells können unter anderem Mutatoren (die von den integrierten Prozedurblöcken verwendet werden), Bildfelder mit Klick-Handlern oder etwas außerhalb von Blockly verwendet werden.

Blöcke zur Toolbox hinzufügen

Die Kategorie der integrierten dynamischen Verfahren von Blockly ist spezifisch für die integrierten Prozedurblöcke von Blockly. Damit Sie auf Ihre Blöcke zugreifen können, müssen Sie Ihre eigene benutzerdefinierte dynamische Kategorie definieren und Ihrer Toolbox hinzufügen.

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