Attribute von Formular-Komponenten binden

Verschiedene Attribute von Formular-Komponenten kann man an Daten im state binden. Bei einer direkten Referenz handelt es sich um Two-Way binding. Sobald sich der Wert einer Komponente ändert, wird automatisch der referenzierte Wert im state aktualisiert und umgekehrt. Andernfalls handelt es sich um One-Way binding. Hierbei wird der Wert nur aus dem state gelesen. So kann z. B. der Wert einer checkbox mit der Sichtbarkeit anderer Komponenten verknüpft werden.

Die Syntax für das Binding orientiert sich an JS Template Strings und JSON Pointer Syntax. Um ein Binding zu deklarieren wird dementsprechend ${} verwendet. Innerhalb der Klammern befindet sich dann ein beliebiger Ausdruck mit reduzierter JS Syntax. Mithilfe von /, ähnlich zur JSON Pointer Syntax, können Elemente aus dem state referenziert werden. Hierbei ist zu beachten, dass es sich jeweils um reduzierte Syntax handelt und nicht der vollständige Funktionsumfang vorhanden ist. JS Funktionsaufrufe sind nicht möglich.

{
  "type": "textfield",
  "label": "Two-Way binding",
  "value": "${/data/text}"
},
{
  "type": "textfield",
  "value": "One-Way binding: ${/data/text}"
}

Pointer können ausschließlich die Zeichen a–z, A–Z, 0–9, Unterstrich und zur Trennung der einzelnen Token / enthalten. Ein einzelner Token referenziert je genau eine Ebene des state Objekts. Der Pointer ${/data/text/example} ergibt somit den Wert Something, wenn der state folgendermaßen definiert ist: "state":{"data":{"text":{"example":"Something"}}}. Der erste Token eines Pointers kann nicht mit 0–9 beginnen. Nachfolgenden Token können mit 0–9 beginnen, solange ausschließlich 0–9 und keine anderen Zeichen innerhalb des Tokens vorkommen. Andere Zeichen wie Umlaute oder Leerzeichen sind für Pointer nicht zulässig.

{
  "type": "textfield",
  "value": "${/data/_a_0/0}"
}

Verknüpfung

Zur Verknüpfung von verschiedenen Werten stehen diverse Operatoren zur Verfügung. Es ist zu beachten, dass eine Auswertung von Operatoren nur innerhalb von ${} möglich ist. Außerhalb wird kein Binding durchgeführt und Operatoren, wie auch alles andere, werden in dem Fall als Text interpretiert.

Innerhalb vom Binding ist der Klammeroperator () verfügbar. Strings, Zahlen, true, false und null können als Vergleichswert benutzt werden.

Boolean

Für boolesche Werte sind folgende Operatoren verfügbar: ! (logisches nicht), && (logisches und), || (logisches oder), ? : (Wenn-Dann-Sonst), ==, ===, !=, !==.

Beispiel bindingBoolean.json
bindingBoolean.json
{
  "$schema": "https://forms.virtimo.net/5.0.x/schema.json",
  "metaData": {
    "id": 0,
    "version": 0
  },
  "configuration": {},
  "components": [
    {
      "type": "container",
      "label": "Boolean Binding",
      "components": [
        {
          "type": "html",
          "value": "Input"
        },
        {
          "type": "checkbox",
          "label": "A",
          "value": "${/data/A}"
        },
        {
          "type": "checkbox",
          "label": "B",
          "value": "${/data/B}"
        },
        {
          "type": "checkbox",
          "label": "C",
          "value": "${/data/C}"
        },
        {
          "type": "textfield",
          "label": "X",
          "labelAlign": "left",
          "value": "${/data/X}"
        },
        {
          "type": "numberfield",
          "label": "Y",
          "labelAlign": "left",
          "value": "${/data/Y}"
        },
        {
          "type": "html",
          "value": "Output"
        },
        {
          "type": "checkbox",
          "label": "Not A",
          "value": "${!/data/A}",
          "disabled": true
        },
        {
          "type": "checkbox",
          "label": "A and B",
          "value": "${/data/A && /data/B}",
          "disabled": true
        },
        {
          "type": "checkbox",
          "label": "A or B",
          "value": "${/data/A || /data/B}",
          "disabled": true
        },
        {
          "type": "checkbox",
          "label": "If A then B else C",
          "value": "${/data/A ? /data/B : /data/C}",
          "disabled": true
        },
        {
          "type": "checkbox",
          "label": "X != 'x' or Y == 1",
          "value": "${(/data/X != 'x') || (/data/Y == 1)}",
          "disabled": true
        }
      ]
    }
  ],
  "state": {
    "data": {
      "A": true,
      "B": false,
      "C": false,
      "X": "a",
      "Y": 0
    }
  }
}

Number

Für Zahlen sind folgende Operatoren verfügbar: +, -, *, /, >, >=, <, <=, ==, ===, !=, !==.

Beispiel bindingNumber.json
bindingNumber.json
{
  "$schema": "https://forms.virtimo.net/5.0.x/schema.json",
  "metaData": {
    "id": 0,
    "version": 0
  },
  "configuration": {},
  "components": [
    {
      "type": "container",
      "label": "Number Binding",
      "components": [
        {
          "type": "html",
          "value": "Input"
        },
        {
          "type": "numberfield",
          "label": "A",
          "value": "${/data/A}"
        },
        {
          "type": "numberfield",
          "label": "B",
          "value": "${/data/B}"
        },
        {
          "type": "numberfield",
          "label": "C",
          "value": "${/data/C}"
        },
        {
          "type": "html",
          "value": "Output"
        },
        {
          "type": "html",
          "value": "A &times; B = ${/data/A * /data/B}"
        },
        {
          "type": "html",
          "value": "A &times; B ${/data/A * /data/B == /data/C ? '=' : '&ne;'} C"
        },
        {
          "type": "html",
          "value": "C &div; (B + A) = ${/data/C / (/data/B + /data/A)}"
        }
      ]
    }
  ],
  "state": {
    "data": {
      "A": 7,
      "B": 6,
      "C": 42
    }
  }
}

String

Um Strings zu kombinieren, kann man einfach mehrere Bindings kombinieren. Alternativ können Strings mit + innerhalb vom Binding konkateniert werden. Dabei muss dann Text in ' eingeschlossen werden.

Beispiel bindingString.json
bindingString.json
{
  "$schema": "https://forms.virtimo.net/5.0.x/schema.json",
  "metaData": {
    "id": 0,
    "version": 0
  },
  "configuration": {},
  "components": [
    {
      "type": "container",
      "label": "String Binding",
      "components": [
        {
          "type": "html",
          "value": "Connecting more than ${/data/A} ${/data/B} values.${/data/Dot}"
        },
        {
          "type": "html",
          "value": "Alternative: ${'Connecting more than ' + /data/A + ' ' + /data/B + ' values' + /data/Dot}"
        }
      ]
    }
  ],
  "state": {
    "data": {
      "A": 2,
      "B": "string",
      "Dot": "."
    }
  }
}

Sonderfälle

Geschweifte Klammern (Escaping)

Wenn ${ benutzt wird, wird dies automatisch als Beginn von einem Binding interpretiert. Binding mit {} ist nicht möglich und sollte nicht verwendet werden, da in dem Fall das komplette Binding unverändert als Text angezeigt wird. Von ${, { und } abgesehen, können beliebige Zeichen als Text verwendet werden. Wenn ${, { oder } als Text vorkommen soll, muss dies innerhalb vom Binding stehen.

{
  "type": "html",
  "value": "Valid display of curly brackets: ${'}' + '${' + '{'}, other symbols: $%/()[]+'#-~1²"
}

Typ-Konvertierung

Komponenten, die einen bestimmten Datentyp erwarten, wie checkbox oder numberfield, führen automatisch eine Typ-Konvertierung durch falls erforderlich. Die automatische Konvertierung von primitiven Datentypen in Strings oder von Strings zu Zahlen oder einem Datum ist unproblematisch. Bei Booleans oder bei der Konvertierung zwischen nicht String Datentypen entspricht die automatische Konvertierung jedoch nicht immer dem gewünschten Ergebnis. Dies sollte daher vermieden werden oder im Fall von Booleans durch einen Operator gelöst werden.

{
  "type": "numberfield",
  "value": "${/data/number}"
},
{
  "type": "checkbox",
  "label": "Explicit Conversion from Number to Boolean",
  "value": "${/data/number > 0}",
  "disabled": true
},
{
  "type": "textfield",
  "label": "Automatic conversion from Number to String",
  "value": "${/data/number}",
  "disabled": true
}

Fehlende Werte

Referenziert ein Pointer einen Pfad, der nicht im state existiert, wird das komplette Binding nicht ausgewertet. Der Wert des entsprechenden Attributs ist in dem Fall null.

Arrays und Nested Bindings

Nested Bindings wie ${/data/items/${/data/index}} sind nicht zulässig. Um mit einem dynamischen Index auf Array-Elemente zuzugreifen, sollte stattdessen eine dafür passende Komponente wie combobox verwendet werden.

Für das Überschreiben von Werten innerhalb eines Arrays sollte die Komponente table verwendet werden. Andere Komponenten zum Darstellen eines einzelnen Wertes wie textfield erwarten zum Überschreiben ein Objekt. Diese Komponenten sollten daher nicht zum Überschreiben von Werten in einem Array verwendet werden.


Keywords: