Arc Rider Docs · Markdown

Developer Kit · Patterns

Data Bag Actions

Data Bag Actions (partielle Updates in JSON-Töpfen)

AI Defaults
Gilt für alle Widgets mit `actions`
, die den Ninox Action Performer nutzen (Button, Layout, Table, KPI Bar, Calendar, Drawing, …).
Syntax:
`dataBag: { encoding, base, patch }` — `patch` folgt JSON Merge Patch (RFC 7386): Key setzen/überschreiben; `null` / `''` löscht den Key. -** `encoding`:** `"raw"` (Default) — Plain-JSON im Textfeld via `JSON.stringify` / `parseJSON`.** `encoding` weglassen**, wenn nicht Legacy-Daten. -** `base`:** aktueller Feldwert (Rohstring aus dem Textfeld oder bereits geparstes Objekt).
Lesen in Formeln:
`parseJSON(text(helper_uiState))` bzw. `parseJSON(helper_data)` — **ohne** `urlDecode`.
Alternative:
plain `value` auf der Action, wenn das Zielfeld nur einen einzelnen Wert hält.
Legacy:
`"urlEncoded"` nur bei bestehenden url-kodierten Feldern (`parseJSON(urlDecode(...))`). Siehe Abschnitt unten.
Nicht verwechseln:
Widget-Prop `dataBag: { key, encoding }` in **arcCustomDrawing** — nur für Shape-Geometrie in verschachtelten JSON-Töpfen. Siehe custom-drawing. ---

Problem

Viele UI-Zustände (Tab, Filter, ausgewählte ID) liegen in einem JSON-Textfeld (State-Bag). Beim Klick soll nur ein Key geändert werden — ohne den Rest zu löschen.

Ohne dataBag musst du in Ninox den Topf manuell lesen, setItem aufrufen und wieder serialisieren (siehe <a href="/docs/ninox/templates/tips_statefield">State-Bag in einem Textfeld</a>).

Mit dataBag auf der Action übernimmt der Action Performer Read → Patch → Write.

Action-Syntax

Partiell patchen (empfohlen für State-Bags)

{
  type: "update",
  recordId: Nr,
  fieldId: fieldId(Nr, "helper_mainContentState"),
  dataBag: {
    base: helper_mainContentState,
    patch: { selectedShape: "123", activeTab: "details" }
  }
}

- base: Rohwert aus dem Feld (String oder bereits geparstes Objekt — der Helfer normalisiert). - patch: nur die Keys, die sich ändern sollen. - Key löschen: patch: { statusFilter: null } oder "". - encoding: optional; Default "raw" — Plain-JSON im Textfeld.

Einfacher Wert (dediziertes Feld)

{
  type: "update",
  recordId: Nr,
  fieldId: fieldId(Nr, "helper_selectedId"),
  value: "123"
}

Wo funktioniert das?

KontextdataBag auf ActionWidget-dataBag (key)
arcCustomButton, Table, KPI Bar, Calendar, Input, …ja (update + create)—
arcCustomCalendarWeek — createNewja (create, inkl. ##newRecordId##)—
arcCustomLayout — Block-actions (Klick)ja (update + create)—
arcCustomLayout — Widget-actions (Klick auf gesamtes Layout)ja (update + create)—
arcCustomDrawing — Shape-actions (Klick)ja (update)—
arcCustomDrawing — Geometrie speichern—ja (key + optional encoding)

Alle Widgets teilen sich dieselbe Action-Engine für update und create (inklusive dataBag).

> Hinweis für arcCustomDrawing Create: Das Drawing-Widget legt neue Shapes über einen eigenen Create-Pfad an, nicht über die üblichen actions. Dort gilt weiterhin setNewRecordId. dataBag auf type: "create" gilt nur für explizit definierte Create-Actions in anderen Widgets (z. B. Button, Layout).

React / Vue / WeWeb: Actions werden an den Host durchgereicht (onAction / emit('action')). dataBag-Patching ist dort nicht automatisch — der Host muss den Patch selbst anwenden oder den Klartext-value nutzen.

dataBag auf create-Actions

Neu: dataBag funktioniert auch auf type: "create". Nach dem Anlegen des Datensatzes wird der Platzhalter ##newRecordId## durch die numerische ID des neuen Records ersetzt — dann wird der Bag gepatch und ins Zielfeld geschrieben.

Platzhalter ##newRecordId##

Ersetzt in beliebiger Tiefe (Strings, Objekte, Arrays) die neue numerische Record-ID nach dem Create.

{
  type: "create",
  tableId: tableId("Aufgaben"),
  changeFieldValues: [{
    fieldId: fieldId("Aufgaben", "Projekt"),
    value: Nr
  }],
  dataBag: {
    recordId: Nr,
    fieldId: fieldId(Nr, "helper_uiState"),
    base: helper_uiState,
    patch: { selectedTaskId: "##newRecordId##" }
  }
}

Ablauf: Create → neue ID → ##newRecordId## auflösen → readBag(base) → applyPatch(bag, patch) → writeBag → database.update

Mehrere Bags nach Create (Array)

dataBag: [
  {
    recordId: Nr,
    fieldId: fieldId(Nr, "helper_uiState"),
    base: helper_uiState,
    patch: { selectedTaskId: "##newRecordId##", activeTab: "details" }
  },
  {
    recordId: parentNr,
    fieldId: fieldId(parentNr, "helper_dashState"),
    base: helper_dashState,
    patch: { lastCreatedId: "##newRecordId##" }
  }
]

Kombiniert mit setNewRecordId

Beide Mechanismen können zusammen genutzt werden:

setNewRecordIddataBag auf Create
ZielfeldNinox-Referenzfeld (Verknüpfung)JSON-Textfeld (State-Bag)
Schreibtnumerische ID direktID als Patch-Key in JSON
WannParent-Datensatz verlinkenUI-State nach Create setzen
{
  type: "create",
  tableId: tableId("Aufgaben"),
  changeFieldValues: [...],
  setNewRecordId: [{ recordId: Nr, fieldId: fieldId(Nr, "LetzteAufgabe") }],
  dataBag: {
    recordId: Nr,
    fieldId: fieldId(Nr, "helper_uiState"),
    base: helper_uiState,
    patch: { selectedTaskId: "##newRecordId##" }
  }
}

Kalender createNew (nach erfolgreichem Anlegen)

createNew.dataBag hängt direkt am Kalender-Create, nicht an einer Button-Action. Nach dem Anlegen (Aufziehen, Ganztags oder Ghost-Klick) läuft derselbe Create-Pfad. Typisch: Auswahl leeren, damit der Placement-Ghost verschwindet.

createNew: {
  tableId: tableId("Termine"),
  dateFrom: fieldId(first(Termine), "Datum von"),
  timeFrom: fieldId(first(Termine), "Von"),
  dateTo: fieldId(first(Termine), "Datum bis"),
  timeTo: fieldId(first(Termine), "Bis"),
  dataBag: {
    recordId: Nr,
    fieldId: fieldId(Nr, "helper_uiState"),
    base: helper_uiState,
    patch: { auswahlId: null, lastTerminId: "##newRecordId##" }
  }
}

Beispiele

Tab wechseln (Button)

arcCustomButton({
  uniqueId: "tab-details-" + Nr,
  title: "Details",
  actions: [{
    type: "update",
    recordId: Nr,
    fieldId: fieldId(Nr, "helper_uiState"),
    dataBag: {
      base: helper_uiState,
      patch: { activeTab: "details" }
    }
  }]
})

Mobile Dimmer schließen (Layout-Block)

blocks: [{
  width: "100%", height: "100%",
  styles: "position: absolute; z-index: 100;",
  value: "",
  actions: [{
    type: "update",
    recordId: Nr,
    fieldId: fieldId(Nr, "helper_mainContentState"),
    dataBag: {
      base: helper_mainContentState,
      patch: { sidebarOpen: "", selectedShape: "" }
    }
  }]
}]

Shape auswählen → Sidebar (Drawing)

actions: [{
  type: "update",
  recordId: stateRecordId,
  fieldId: stateField,
  dataBag: {
    base: helper_mainContentState,
    patch: { selectedShape: Nr }
  }
}]

Ausführlich: <a href="../widgets/custom-drawing.md"><code>custom-drawing</code></a> (Shape-actions, Klick vs. Drag).

Legacy: urlEncoded

Früher wurde JSON oft mit urlEncode(formatJSON(...)) geschrieben, weil Ninox-Actions mit {} in value problematisch waren. Mit dataBag (Patch im Bundle) ist das in neuen Projekten nicht mehr nötig.

raw (Default)urlEncoded (Legacy)
Im Feld{"activeTab":"details"}%7B%22activeTab%22%3A...
Formel lesenparseJSON(text(feld))parseJSON(urlDecode(text(feld)))
Action schreibendataBag: { base, patch }zusätzlich encoding: "urlEncoded"
Wannneue Demos, Seeds, Triggerbestehende Kundendaten

Siehe auch

  • <a href="/docs/ninox/templates/tips_statefield">State-Bag in einem Textfeld</a> — State-Bag manuell mit Ninox setItem / item
  • <a href="../widgets/custom-drawing.md"><code>custom-drawing</code></a> — Widget-dataBag für Pin-Geometrie + Shape-Actions