Benutzerdefiniertes Feld erstellen

Bevor Sie einen neuen Feldtyp erstellen, sollten Sie prüfen, ob eine der anderen Methoden zum Anpassen von Feldern Ihren Anforderungen entspricht. Wenn in Ihrer Anwendung ein neuer Werttyp gespeichert werden muss oder Sie eine neue Benutzeroberfläche für einen vorhandenen Werttyp erstellen möchten, müssen Sie wahrscheinlich einen neuen Feldtyp erstellen.

So erstellen Sie ein neues Feld:

  1. Implementieren Sie einen Konstruktor.
  2. Registrieren Sie einen JSON-Schlüssel und implementieren Sie fromJson.
  3. Initialisierung der On-Block-UI und Ereignis-Listener verwalten
  4. Entsorgung von Ereignis-Listenern verwalten (die Entsorgung der Benutzeroberfläche wird für Sie erledigt).
  5. Wertbehandlung implementieren
  6. Fügen Sie aus Gründen der Barrierefreiheit eine Textdarstellung des Feldwerts hinzu.
  7. Fügen Sie zusätzliche Funktionen hinzu, z. B.:
  8. Konfigurieren Sie weitere Aspekte des Felds, z. B.:

In diesem Abschnitt wird davon ausgegangen, dass Sie den Artikel Anatomy of a Field (Anatomie eines Felds) gelesen und verstanden haben.

Ein Beispiel für ein benutzerdefiniertes Feld finden Sie in der Demo zu benutzerdefinierten Feldern.

Konstruktor implementieren

Der Konstruktor des Felds ist für die Einrichtung des Anfangswerts des Felds und optional für die Einrichtung eines lokalen Validators verantwortlich. Der Konstruktor des benutzerdefinierten Felds wird während der Initialisierung des Quellblocks aufgerufen, unabhängig davon, ob der Quellblock in JSON oder JavaScript definiert ist. Das benutzerdefinierte Feld hat also während der Erstellung keinen Zugriff auf den Quellblock.

Im folgenden Codebeispiel wird ein benutzerdefiniertes Feld mit dem Namen GenericField erstellt:

class GenericField extends Blockly.Field {
  constructor(value, validator) {
    super(value, validator);

    this.SERIALIZABLE = true;
  }
}

Methodensignatur

Feldkonstruktoren nehmen in der Regel einen Wert und einen lokalen Validator an. Der Wert ist optional. Wenn Sie keinen Wert übergeben (oder einen Wert übergeben, der die Klassenüberprüfung nicht besteht), wird der Standardwert der Superklasse verwendet. Für die Standardklasse Field ist das null. Wenn Sie diesen Standardwert nicht verwenden möchten, müssen Sie einen geeigneten Wert übergeben. Der Validatorparameter ist nur für bearbeitbare Felder vorhanden und wird in der Regel als optional gekennzeichnet. Weitere Informationen zu Validatoren finden Sie in den Validator-Dokumenten.

Struktur

Die Logik in Ihrem Konstruktor sollte diesem Ablauf folgen:

  1. Rufen Sie den übergeordneten Konstruktor auf (alle benutzerdefinierten Felder sollten von Blockly.Field oder einer ihrer Unterklassen abgeleitet sein), um den Wert richtig zu initialisieren und den lokalen Validator für Ihr Feld festzulegen.
  2. Wenn Ihr Feld serialisierbar ist, legen Sie die entsprechende Eigenschaft im Konstruktor fest. Bearbeitbare Felder müssen serialisierbar sein. Da Felder standardmäßig bearbeitbar sind, sollten Sie diese Property auf „wahr“ setzen, es sei denn, Sie wissen, dass sie nicht serialisierbar sein soll.
  3. Optional: Sie können zusätzliche Anpassungen vornehmen. Beispielsweise können Sie über Labelfelder eine CSS-Klasse übergeben, die dann auf den Text angewendet wird.

JSON und Registrierung

In JSON-Blockdefinitionen werden Felder durch einen String beschrieben (z.B. field_number, field_textinput). Blockly verwaltet eine Zuordnung dieser Strings zu Feldobjekten und ruft während der Konstruktion fromJson für das entsprechende Objekt auf.

Rufen Sie Blockly.fieldRegistry.register auf, um dieser Karte den Feldtyp hinzuzufügen, und übergeben Sie die Feldklasse als zweites Argument:

Blockly.fieldRegistry.register('field_generic', GenericField);

Außerdem müssen Sie Ihre fromJson-Funktion definieren. In Ihrer Implementierung sollten Sie zuerst alle Verweise auf Lokalisierungstokens mit replaceMessageReferences auflösen und dann die Werte an den Konstruktor übergeben.

GenericField.fromJson = function(options) {
  const value = Blockly.utils.parsing.replaceMessageReferences(
      options['value']);
  return new CustomFields.GenericField(value);
};

Wird initialisiert

Wenn das Feld erstellt wird, enthält es im Grunde nur einen Wert. Bei der Initialisierung wird das DOM erstellt, das Modell erstellt (falls das Feld ein Modell hat) und Ereignisse gebunden.

Blockanzeige

Während der Initialisierung müssen Sie alles erstellen, was Sie für die Blockanzeige des Felds benötigen.

Standardeinstellungen, Hintergrund und Text

Mit der Standardfunktion initView werden ein helles rect-Element und ein text-Element erstellt. Wenn Ihr Feld beides und noch einige zusätzliche Funktionen haben soll, rufen Sie die initView-Funktion der Superklasse auf, bevor Sie die restlichen DOM-Elemente hinzufügen. Wenn Ihr Feld nur eines dieser Elemente enthalten soll, können Sie die Funktionen createBorderRect_ oder createTextElement_ verwenden.

DOM-Aufbau anpassen

Wenn es sich bei Ihrem Feld um ein generisches Textfeld handelt (z.B. Textinput), wird die DOM-Erstellung für Sie übernommen. Andernfalls müssen Sie die initView-Funktion überschreiben, um die DOM-Elemente zu erstellen, die Sie beim zukünftigen Rendern des Felds benötigen.

Ein Drop-down-Feld kann beispielsweise sowohl Bilder als auch Text enthalten. In initView werden ein einzelnes Bildelement und ein einzelnes Textelement erstellt. Während render_ wird dann je nach Typ der ausgewählten Option das aktive Element angezeigt und das andere ausgeblendet.

DOM-Elemente können entweder mit der Methode Blockly.utils.dom.createSvgElement oder mit herkömmlichen Methoden zum Erstellen von DOM-Elementen erstellt werden.

Für die Blockanzeige eines Felds gelten folgende Anforderungen:

  • Alle DOM-Elemente müssen untergeordnete Elemente des fieldGroup_-Elements des Felds sein. Die Feldgruppe wird automatisch erstellt.
  • Alle DOM-Elemente müssen sich innerhalb der angegebenen Abmessungen des Felds befinden.

Weitere Informationen zum Anpassen und Aktualisieren von Anzeigen im Block finden Sie im Abschnitt Rendering.

Textsymbole hinzufügen

Wenn Sie dem Text eines Felds Symbole hinzufügen möchten, z. B. das Gradsymbol für das Feld Winkel, können Sie das Symbolelement (normalerweise in einer <tspan> enthalten) direkt an das textElement_ des Felds anhängen.

Eingabe-Ereignisse

Standardmäßig werden für Felder Tooltip-Ereignisse und mousedown-Ereignisse registriert, die zum Anzeigen von Editoren verwendet werden. Wenn Sie auf andere Arten von Ereignissen reagieren möchten (z.B. wenn Sie das Ziehen eines Feldes verarbeiten möchten), sollten Sie die bindEvents_-Funktion des Felds überschreiben.

bindEvents_() {
  // Call the superclass function to preserve the default behavior as well.
  super.bindEvents_();

  // Then register your own additional event listeners.
  this.mouseDownWrapper_ =
  Blockly.browserEvents.conditionalBind(this.getClickTarget_(), 'mousedown', this,
      function(event) {
        this.originalMouseX_ = event.clientX;
        this.isMouseDown_ = true;
        this.originalValue_ = this.getValue();
        event.stopPropagation();
      }
  );
  this.mouseMoveWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mousemove', this,
      function(event) {
        if (!this.isMouseDown_) {
          return;
        }
        var delta = event.clientX - this.originalMouseX_;
        this.setValue(this.originalValue_ + delta);
      }
  );
  this.mouseUpWrapper_ =
    Blockly.browserEvents.conditionalBind(document, 'mouseup', this,
      function(_event) {
        this.isMouseDown_ = false;
      }
  );
}

Zum Binden an ein Ereignis sollten Sie im Allgemeinen die Funktion Blockly.utils.browserEvents.conditionalBind verwenden. Bei dieser Methode der Ereignisbindung werden sekundäre Berührungen während des Ziehens herausgefiltert. Wenn Ihr Handler auch während eines laufenden Ziehens ausgeführt werden soll, können Sie die Funktion Blockly.browserEvents.bind verwenden.

Entsorgung

Wenn Sie benutzerdefinierte Ereignisempfänger in der bindEvents_-Funktion des Felds registriert haben, müssen Sie sie in der dispose-Funktion abmelden.

Wenn Sie die Ansicht des Felds korrekt initialisiert haben, indem Sie alle DOM-Elemente an fieldGroup_ angehängt haben, wird das DOM des Felds automatisch entfernt.

Umgang mit Werten

→ Weitere Informationen zum Unterschied zwischen dem Wert und dem Text eines Felds finden Sie unter Anatomie eines Felds.

Validierungsreihenfolge

Flussdiagramm, das die Reihenfolge beschreibt, in der Validator ausgeführt werden

Klassenvalidator implementieren

Felder sollten nur bestimmte Werte akzeptieren. Beispielsweise sollten in Zahlenfeldern nur Zahlen und in Farbfeldern nur Farben eingegeben werden können. Dies wird durch klassen- und lokale Validator sichergestellt. Für den Klassenvalidierer gelten dieselben Regeln wie für lokale Validierer, mit der Ausnahme, dass er auch im Konstruktor ausgeführt wird und daher nicht auf den Quellblock verweisen darf.

Wenn Sie den Klassenvalidierer Ihres Felds implementieren möchten, überschreiben Sie die Funktion doClassValidation_.

doClassValidation_(newValue) {
  if (typeof newValue != 'string') {
    return null;
  }
  return newValue;
};

Gültige Werte verarbeiten

Wenn der an ein Feld mit setValue übergebene Wert gültig ist, erhalten Sie einen doValueUpdate_-Callback. Standardmäßig gilt für die Funktion doValueUpdate_ Folgendes:

  • Die Property value_ wird auf newValue gesetzt.
  • Die Property isDirty_ wird auf true festgelegt.

Wenn Sie den Wert einfach speichern und keine benutzerdefinierte Verarbeitung vornehmen möchten, müssen Sie doValueUpdate_ nicht überschreiben.

Andernfalls haben Sie folgende Möglichkeiten:

  • Benutzerdefinierter Speicherplatz: newValue
  • Andere Attribute basierend auf newValue ändern
  • Speichern Sie, ob der aktuelle Wert gültig ist oder nicht.

Sie müssen doValueUpdate_ überschreiben:

doValueUpdate_(newValue) {
  super.doValueUpdate_(newValue);
  this.displayValue_ = newValue;
  this.isValueValid_ = true;
}

Umgang mit ungültigen Werten

Wenn der an das Feld mit setValue übergebene Wert ungültig ist, erhalten Sie einen doValueInvalid_-Callback. Standardmäßig führt die Funktion doValueInvalid_ nichts aus. Das bedeutet, dass ungültige Werte standardmäßig nicht angezeigt werden. Außerdem wird das Feld nicht noch einmal gerendert, da die Eigenschaft isDirty_ nicht festgelegt wird.

Wenn Sie ungültige Werte anzeigen lassen möchten, sollten Sie doValueInvalid_ überschreiben. In den meisten Fällen sollten Sie für displayValue_ den Wert „ungültig“ festlegen, isDirty_ auf true setzen und override render_ aktivieren, damit die Anzeige im Block anhand von displayValue_ statt value_ aktualisiert wird.

doValueInvalid_(newValue) {
  this.displayValue_ = newValue;
  this.isDirty_ = true;
  this.isValueValid_ = false;
}

Mehrteilige Werte

Wenn Ihr Feld einen mehrteiligen Wert enthält (z.B. Listen, Vektoren, Objekte), können Sie festlegen, dass die Teile wie einzelne Werte behandelt werden sollen.

doClassValidation_(newValue) {
  if (FieldTurtle.PATTERNS.indexOf(newValue.pattern) == -1) {
    newValue.pattern = null;
  }

  if (FieldTurtle.HATS.indexOf(newValue.hat) == -1) {
    newValue.hat = null;
  }

  if (FieldTurtle.NAMES.indexOf(newValue.turtleName) == -1) {
    newValue.turtleName = null;
  }

  if (!newValue.pattern || !newValue.hat || !newValue.turtleName) {
    this.cachedValidatedValue_ = newValue;
    return null;
  }
  return newValue;
}

Im obigen Beispiel wird jedes Attribut von newValue einzeln geprüft. Wenn dann am Ende der doClassValidation_-Funktion eine einzelne Property ungültig ist, wird der Wert im Cache der Property cacheValidatedValue_ gespeichert, bevor null (ungültig) zurückgegeben wird. Wenn das Objekt mit einzeln validierten Eigenschaften im Cache gespeichert wird, kann die Funktion doValueInvalid_ sie separat verarbeiten, indem einfach eine !this.cacheValidatedValue_.property-Prüfung durchgeführt wird, anstatt jede Eigenschaft einzeln noch einmal zu validieren.

Dieses Muster zur Validierung mehrteiliger Werte kann auch in lokalen Validierungsmethoden verwendet werden. Derzeit gibt es jedoch keine Möglichkeit, dieses Muster durchzusetzen.

isDirty_

isDirty_ ist ein Flag, das in der Funktion setValue und in anderen Teilen des Felds verwendet wird, um anzugeben, ob das Feld neu gerendert werden muss. Wenn sich der Anzeigewert des Felds geändert hat, sollte isDirty_ normalerweise auf true gesetzt werden.

Text

→ Informationen dazu, wo der Text eines Felds verwendet wird und wie er sich vom Wert des Felds unterscheidet, finden Sie unter Anatomie eines Felds.

Wenn sich der Text des Felds vom Wert des Felds unterscheidet, sollten Sie die Funktion getText überschreiben, um den richtigen Text anzugeben.

getText() {
  let text = this.value_.turtleName + ' wearing a ' + this.value_.hat;
  if (this.value_.hat == 'Stovepipe' || this.value_.hat == 'Propeller') {
    text += ' hat';
  }
  return text;
}

Mitbearbeiter erstellen

Wenn Sie die Funktion showEditor_ definieren, achtet Blockly automatisch auf Klicks und ruft showEditor_ zum richtigen Zeitpunkt auf. Sie können beliebigen HTML-Code in Ihrem Editor anzeigen, indem Sie ihn in eines der beiden speziellen divs einfügen, DropDownDiv und WidgetDiv, die über der restlichen Blockly-Benutzeroberfläche schweben.

Mit DropDownDiv können Sie Editoren in einem Feld bereitstellen, das mit einem Feld verbunden ist. Sie positioniert sich automatisch in der Nähe des Feldes, bleibt aber innerhalb der sichtbaren Grenzen. Die Winkelauswahl und die Farbauswahl sind gute Beispiele für die DropDownDiv.

Bild der Winkelauswahl

Mit WidgetDiv können Sie Editoren bereitstellen, die nicht in einem Feld angezeigt werden. Bei Zahlenfeldern wird das Feld mit dem WidgetDiv über ein HTML-Texteingabefeld abgedeckt. Das DropDownDiv übernimmt die Positionierung für Sie, das WidgetDiv hingegen nicht. Die Elemente müssen manuell positioniert werden. Das Koordinatensystem ist in Pixelkoordinaten relativ zum linken oberen Fensterrand. Der Editor für die Texteingabe ist ein gutes Beispiel für die WidgetDiv.

Bild des Texteditors

showEditor_() {
  // Create the widget HTML
  this.editor_ = this.dropdownCreate_();
  Blockly.DropDownDiv.getContentDiv().appendChild(this.editor_);

  // Set the dropdown's background colour.
  // This can be used to make it match the colour of the field.
  Blockly.DropDownDiv.setColour('white', 'silver');

  // Show it next to the field. Always pass a dispose function.
  Blockly.DropDownDiv.showPositionedByField(
      this, this.disposeWidget_.bind(this));
}

Beispielcode für WidgetDiv

showEditor_() {
  // Show the div. This automatically closes the dropdown if it is open.
  // Always pass a dispose function.
  Blockly.WidgetDiv.show(
    this, this.sourceBlock_.RTL, this.widgetDispose_.bind(this));

  // Create the widget HTML.
  var widget = this.createWidget_();
  Blockly.WidgetDiv.getDiv().appendChild(widget);
}

Bereinigen

Sowohl das DropDownDiv als auch das WidgetDiv übernehmen das Löschen der HTML-Elemente des Widgets. Alle Ereignis-Listener, die Sie auf diese Elemente angewendet haben, müssen Sie jedoch manuell entfernen.

widgetDispose_() {
  for (let i = this.editorListeners_.length, listener;
      listener = this.editorListeners_[i]; i--) {
    Blockly.browserEvents.unbind(listener);
    this.editorListeners_.pop();
  }
}

Die Funktion dispose wird im null-Kontext auf der DropDownDiv aufgerufen. Auf dem WidgetDiv wird es im Kontext des WidgetDiv aufgerufen. In beiden Fällen ist es am besten, die Funktion bind zu verwenden, wenn Sie eine Dispose-Funktion übergeben, wie in den Beispielen DropDownDiv und WidgetDiv oben gezeigt.

→ Informationen zur Entsorgung, die nicht speziell auf die Entsorgung von Editoren bezogen sind, finden Sie unter Entsorgung.

Blockanzeige aktualisieren

Mit der Funktion render_ wird die Blockanzeige des Felds so aktualisiert, dass sie dem internen Wert entspricht.

Beispiele:

  • Text ändern (Drop-down-Menü)
  • Farbe ändern (color)

Standardeinstellungen

Die Standardfunktion render_ legt den angezeigten Text auf das Ergebnis der Funktion getDisplayText_ fest. Die Funktion getDisplayText_ gibt die value_-Eigenschaft des Felds zurück, die in einen String umgewandelt wurde, nachdem sie auf die maximale Textlänge gekürzt wurde.

Wenn Sie die Standardanzeige im Block verwenden und das Standardtextverhalten für Ihr Feld funktioniert, müssen Sie render_ nicht überschreiben.

Wenn das Standardtextverhalten für Ihr Feld funktioniert, das Block-Display Ihres Felds aber zusätzliche statische Elemente enthält, können Sie die Standardfunktion render_ aufrufen. Sie müssen sie jedoch trotzdem überschreiben, um die Größe des Felds zu aktualisieren.

Wenn das Standardtextverhalten für Ihr Feld nicht funktioniert oder das On-Block-Display Ihres Felds zusätzliche dynamische Elemente enthält, müssen Sie die Funktion render_ anpassen.

Flussdiagramm, das beschreibt, wie entschieden wird, ob render_ überschrieben werden soll

Rendering anpassen

Wenn das Standard-Rendering-Verhalten für Ihr Feld nicht funktioniert, müssen Sie ein benutzerdefiniertes Rendering-Verhalten definieren. Das kann von der Einstellung benutzerdefinierten Anzeigetexts über das Ändern von Bildelementen bis hin zum Aktualisieren von Hintergrundfarben reichen.

Alle Änderungen an DOM-Attributen sind zulässig. Beachten Sie dabei Folgendes:

  1. Die Erstellung des DOM sollte während der Initialisierung erfolgen, da dies effizienter ist.
  2. Sie sollten immer die Property „size_“ so aktualisieren, dass sie der Größe des On-Block-Displays entspricht.
render_() {
  switch(this.value_.hat) {
    case 'Stovepipe':
      this.stovepipe_.style.display = '';
      break;
    case 'Crown':
      this.crown_.style.display = '';
      break;
    case 'Mask':
      this.mask_.style.display = '';
      break;
    case 'Propeller':
      this.propeller_.style.display = '';
      break;
    case 'Fedora':
      this.fedora_.style.display = '';
      break;
  }

  switch(this.value_.pattern) {
    case 'Dots':
      this.shellPattern_.setAttribute('fill', 'url(#polkadots)');
      break;
    case 'Stripes':
      this.shellPattern_.setAttribute('fill', 'url(#stripes)');
      break;
    case 'Hexagons':
      this.shellPattern_.setAttribute('fill', 'url(#hexagons)');
      break;
  }

  this.textContent_.nodeValue = this.value_.turtleName;

  this.updateSize_();
}

Größe aktualisieren

Das Aktualisieren der size_-Eigenschaft eines Felds ist sehr wichtig, da sie dem Block-Rendering-Code mitteilt, wie das Feld positioniert werden soll. Am besten finden Sie durch Ausprobieren heraus, wie hoch size_ sein sollte.

updateSize_() {
  const bbox = this.movableGroup_.getBBox();
  let width = bbox.width;
  let height = bbox.height;
  if (this.borderRect_) {
    width += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    height += this.constants_.FIELD_BORDER_RECT_X_PADDING * 2;
    this.borderRect_.setAttribute('width', width);
    this.borderRect_.setAttribute('height', height);
  }
  // Note how both the width and the height can be dynamic.
  this.size_.width = width;
  this.size_.height = height;
}

Passende Blockfarben

Wenn Sie möchten, dass die Elemente Ihres Felds mit den Farben des Blocks übereinstimmen, an dem sie angehängt sind, sollten Sie die applyColour-Methode überschreiben. Sie sollten über das Style-Attribut des Blocks auf die Farbe zugreifen.

applyColour() {
  const sourceBlock = this.sourceBlock_;
  if (sourceBlock.isShadow()) {
    this.arrow_.style.fill = sourceBlock.style.colourSecondary;
  } else {
    this.arrow_.style.fill = sourceBlock.style.colourPrimary;
  }
}

Bearbeitbarkeit aktualisieren

Mit der Funktion updateEditable können Sie festlegen, wie das Feld angezeigt wird, je nachdem, ob es bearbeitbar ist oder nicht. Die Standardfunktion sorgt dafür, dass der Hintergrund eine Hover-Antwort (Rahmen) hat oder nicht, wenn er bearbeitbar ist oder nicht. Die Größe der Blockanzeige sollte sich nicht ändern, je nachdem, ob sie bearbeitet werden kann. Alle anderen Änderungen sind jedoch zulässig.

updateEditable() {
  if (!this.fieldGroup_) {
    // Not initialized yet.
    return;
  }
  super.updateEditable();

  const group = this.getClickTarget_();
  if (!this.isCurrentlyEditable()) {
    group.style.cursor = 'not-allowed';
  } else {
    group.style.cursor = this.CURSOR;
  }
}

Serialisierung

Bei der Speicherung wird der Status des Felds gespeichert, damit es später wieder in den Arbeitsbereich geladen werden kann.

Der Status Ihres Arbeitsbereichs enthält immer den Wert des Felds, kann aber auch andere Status enthalten, z. B. den Status der Benutzeroberfläche des Felds. Wenn Ihr Feld beispielsweise eine zoombare Karte ist, auf der Nutzer Länder auswählen können, können Sie auch die Zoomstufe serialisieren.

Wenn Ihr Feld serialisierbar ist, müssen Sie die Property SERIALIZABLE auf true festlegen.

Blockly bietet zwei Serialization Hooks für Felder. Ein Paar Hooks funktioniert mit dem neuen JSON-Serialisierungssystem und das andere Paar mit dem alten XML-Serialisierungssystem.

saveState und loadState

saveState und loadState sind Serialization Hooks, die mit dem neuen JSON-Serialisierungssystem funktionieren.

In einigen Fällen müssen Sie diese nicht angeben, da die Standardimplementierungen funktionieren. Wenn (1) Ihr Feld eine direkte Unterklasse der Basisklasse Blockly.Field ist, (2) Ihr Wert ein JSON-serialisierbarer Typ ist und (3) Sie den Wert nur serialisieren müssen, funktioniert die Standardimplementierung einwandfrei.

Andernfalls sollte Ihre saveState-Funktion ein JSON-serialisierbares Objekt/einen JSON-serialisierbaren Wert zurückgeben, der den Status des Felds darstellt. Die loadState-Funktion sollte dasselbe serialisierbare JSON-Objekt/denselben serialisierbaren JSON-Wert akzeptieren und auf das Feld anwenden.

saveState() {
  return {
    'country': this.getValue(),  // Value state
    'zoom': this.getZoomLevel(), // UI state
  };
}

loadState(state) {
  this.setValue(state['country']);
  this.setZoomLevel(state['zoom']);
}

Vollständige Serialization und Sicherung von Daten

saveState empfängt außerdem den optionalen Parameter doFullSerialization. Diese Option wird für Felder verwendet, die normalerweise auf einen Zustand verweisen, der von einem anderen Serializer serialisiert wurde (z. B. Back-End-Datenmodelle). Der Parameter signalisiert, dass der referenzierte Status nicht verfügbar sein wird, wenn der Block deserialisiert wird. Daher sollte die gesamte Serialization vom Feld selbst durchgeführt werden. Das ist beispielsweise der Fall, wenn ein einzelner Block serialisiert oder ein Block kopiert und eingefügt wird.

Zwei gängige Anwendungsfälle dafür:

  • Wenn ein einzelner Block in einen Arbeitsbereich geladen wird, in dem das zugrunde liegende Datenmodell nicht vorhanden ist, enthält das Feld in seinem eigenen Status genügend Informationen, um ein neues Datenmodell zu erstellen.
  • Wenn ein Block kopiert und eingefügt wird, wird für das Feld immer ein neues Datenmodell erstellt, anstatt auf ein vorhandenes zu verweisen.

Ein Beispiel hierfür ist das integrierte Variablenfeld. Normalerweise wird die ID der Variablen serialisiert, auf die verwiesen wird. Wenn doFullSerialization jedoch wahr ist, wird der gesamte Status serialisiert.

saveState(doFullSerialization) {
  const state = {'id': this.variable_.getId()};
  if (doFullSerialization) {
    state['name'] = this.variable_.name;
    state['type'] = this.variable_.type;
  }
  return state;
}

loadState(state) {
  const variable = Blockly.Variables.getOrCreateVariablePackage(
      this.getSourceBlock().workspace,
      state['id'],
      state['name'],   // May not exist.
      state['type']);  // May not exist.
  this.setValue(variable.getId());
}

So wird sichergestellt, dass bei der Datenaufnahme in einen Arbeitsbereich, in dem die Variable nicht vorhanden ist, eine neue Variable erstellt werden kann, auf die verwiesen werden kann.

toXml und fromXml

toXml und fromXml sind Serialization Hooks, die mit dem alten XML-Serialisierungssystem funktionieren. Verwende diese Hooks nur, wenn es unbedingt notwendig ist (z. B. wenn du an einer alten Codebasis arbeitest, die noch nicht migriert wurde). Verwende ansonsten saveState und loadState.

Die toXml-Funktion sollte einen XML-Knoten zurückgeben, der den Status des Felds darstellt. Die fromXml-Funktion sollte denselben XML-Knoten akzeptieren und auf das Feld anwenden.

toXml(fieldElement) {
  fieldElement.textContent = this.getValue();
  fieldElement.setAttribute('zoom', this.getZoomLevel());
  return fieldElement;
}

fromXml(fieldElement) {
  this.setValue(fieldElement.textContent);
  this.setZoomLevel(fieldElement.getAttribute('zoom'));
}

Bearbeitbare und serialisierbare Properties

Mit der Property EDITABLE wird festgelegt, ob das Feld eine Benutzeroberfläche haben soll, die angibt, dass damit interagiert werden kann. Der Standardwert ist true.

Die Property SERIALIZABLE gibt an, ob das Feld serialisiert werden soll. Der Standardwert ist false. Wenn diese Eigenschaft true ist, müssen Sie möglicherweise Funktionen zum Serialisieren und Deserialisieren angeben (siehe Serialisierung).

Cursor anpassen

Mit der Property CURSOR wird der Cursor festgelegt, der angezeigt wird, wenn Nutzer den Mauszeiger auf Ihr Feld bewegen. Es muss ein gültiger CSS-Cursor-String sein. Standardmäßig ist dies der von .blocklyDraggable definierte Cursor, der Cursor zum Verschieben.