Skip to main content
V2 data forms build on the same card components as V1, so migration is primarily about restructuring your form definition from the flat YAML card array into the V2 node tree. The underlying components, their properties, and their behavior remain the same. What changes is how you configure them, with V2 adding bindings, events, scripting, and conditional rendering on top. This guide walks through the mapping from V1 fields to V2 fields, provides before/after examples for common patterns, highlights what stays the same, and introduces the new capabilities you gain by migrating. If you are building a new form from scratch, start with the Getting Started guide instead.

When to Migrate

You do not need to migrate existing V1 forms. They continue to work unchanged. Consider migrating when:
  • You need dynamic data binding (expressions that reference live data)
  • You want scripted event handlers
  • You need conditional rendering based on data state
  • You want to iterate over data objects with for loops
  • You are building new forms that benefit from V2 features

Field Mapping

The following table shows how V1 card fields map to V2 UINode fields:
V1 FieldV2 FieldNotes
typecomponentPrefixed with namespace: "label" becomes "card:label" (or just "label", resolved via alias)
idkeyUsed for stable DOM identity. Also consider ref if you need programmatic access.
propertiespropsStatic properties. Same keys and values.
properties (dynamic)bindingsMove computed or dynamic values from properties into bindings as JavaScript expressions.
layoutprops (or style/class)V1 grid layout is passed through props for card components that expect it.
childrenchildrenSame concept, same field name.
(none)bindingsNew in V2: dynamic expressions evaluated against the data context.
(none)computedNew in V2: props derived from named scripts.
(none)eventsNew in V2: declarative event handlers.
(none)if / showNew in V2: conditional rendering and visibility.
(none)forNew in V2: list iteration.

Before/After Examples

Simple Label

- type: label
  id: header
  properties:
    label: "Customer Information"
  layout:
    x: 0
    y: 0
    w: 12
    h: 1

Card Panel with Children

- type: cardPanel
  id: invoicePanel
  properties:
    title: "Invoice Details"
    showHeader: true
    groupTaxon: "invoice"
    useTabs: true
  layout:
    x: 0
    y: 0
    w: 12
    h: 8
  children:
    - type: dataAttributeEditor
      id: invoiceNumber
      properties:
        taxon: "invoice/invoiceNumber"
    - type: dataAttributeEditor
      id: totalAmount
      properties:
        taxon: "invoice/totalAmount"

Data Store Grid

- type: dataStoreGrid
  id: customerGrid
  properties:
    dataStoreRef: "customers"
  layout:
    x: 0
    y: 2
    w: 12
    h: 10

Transposed Grid Rollup

- type: transposedGridRollup
  id: financials
  properties:
    groupTaxon: "financialStatement"
    rollupConfig:
      enabled: true
  layout:
    x: 0
    y: 0
    w: 12
    h: 15

What Stays the Same

The following aspects are identical between V1 and V2:

Card Components

All V1 card components are available in V2. The same Vue components are used under the hood. Component types, their accepted properties, and their visual rendering are unchanged.

Property Names and Values

Card properties use the same names and accept the same values. groupTaxon, taxon, useTabs, showHeader, dataStoreRef — all work exactly as before.

Nested Children

Container components (cardPanel, cardGroup, tabs) support children in both V1 and V2.

Data Attribute Editor Behavior

The dataAttributeEditor component works identically. It reads from and writes to the workspace store in the same way.

What’s New in V2

V2 adds several capabilities that are not available in V1:

Bindings

Dynamic expressions that evaluate against the data context. Replace static property values with live data.
{
  "component": "card:label",
  "bindings": {
    "label": "'Total invoices: ' + ctx.dataObjects?.filter(o => o.path === 'invoice').length"
  }
}
See Data Binding for the full guide.

Scripts

Named reusable functions registered at the form level and invoked via computed bindings or scriptRef event handlers.
{
  "scripts": {
    "formatTotal": "function(ctx) { return '$' + Number(ctx.value).toFixed(2); }"
  }
}
See Scripting for the full guide.

Events

Declarative event handlers that respond to component interactions with script execution, event emission, and more.
{
  "events": {
    "click": {
      "type": "script",
      "target": "kodexa.navigation.focusAttribute(ctx.$item.uuid, 'invoice/invoiceNumber')"
    }
  }
}
See Event Handling for the full guide.

Conditional Rendering

Show or hide nodes based on data state:
{
  "component": "card:cardPanel",
  "if": "ctx.dataObjects?.some(o => o.path === 'invoice')",
  "props": { "title": "Invoice Found" }
}

List Iteration

Render a node for each item in a collection:
{
  "component": "card:label",
  "for": {
    "source": "ctx.dataObjects?.filter(o => o.path === 'lineItem')",
    "itemAs": "$item",
    "key": "$item.uuid"
  },
  "bindings": {
    "label": "$item.path"
  }
}

Bridge API

Scripts can interact with the platform through the kodexa.* bridge, providing controlled access to data, navigation, form state, and HTTP methods. See Bridge API for the full reference.

Migration Steps

1

Set the version

Add "version": "2" to your form definition.
2

Convert cards to nodes

Replace the cards array with a nodes array. For each card:
  • Change type to component (optionally adding the card: prefix)
  • Change id to key
  • Change properties to props
  • Keep children as-is
3

Move dynamic values to bindings

Identify any properties that should be computed from data. Move them from props to bindings and write them as JavaScript expressions.
4

Add event handlers

Add events entries for any interaction behavior you want. Start with logging to verify events fire correctly.
5

Add conditional rendering

Use if and show to conditionally display sections based on data state.
6

Extract reusable logic into scripts

Move complex or repeated expressions into the scripts registry and reference them via computed or scriptRef.
7

Configure the bridge

Add a bridge section with the permissions your scripts need.
8

Test

Verify the form renders correctly, bindings update with data changes, and event handlers fire as expected.

Full Migration Example

Here is a complete V1 form and its V2 equivalent:
name: "Invoice Review"
description: "Review and approve invoices"
cards:
  - type: label
    id: title
    properties:
      label: "Invoice Review"
    layout:
      x: 0
      y: 0
      w: 12
      h: 1

  - type: cardPanel
    id: invoiceDetails
    properties:
      title: "Invoice Details"
      showHeader: true
      groupTaxon: "invoice"
    layout:
      x: 0
      y: 1
      w: 6
      h: 8
    children:
      - type: dataAttributeEditor
        id: vendorName
        properties:
          taxon: "invoice/vendorName"
      - type: dataAttributeEditor
        id: invoiceNumber
        properties:
          taxon: "invoice/invoiceNumber"
      - type: dataAttributeEditor
        id: totalAmount
        properties:
          taxon: "invoice/totalAmount"

  - type: taxonGrid
    id: lineItems
    properties:
      groupTaxon: "lineItem"
    layout:
      x: 6
      y: 1
      w: 6
      h: 8

  - type: exceptions
    id: errors
    layout:
      x: 0
      y: 9
      w: 12
      h: 4
Notice how the V2 version adds dynamic behavior (binding the label to show invoice count, click-to-navigate on the vendor name, conditional rendering of the line items grid) while keeping the same component structure.

Next Steps