Skip to content

forms(qml): prefill(values) / prefillFromJson(text) -- load a stored record into DynamicForm for editing (fixes #814) - #817

Merged
Yaraslaut merged 1 commit into
feature/forms-object-array-slotfrom
feature/forms-prefill-from-json
Sep 25, 2026
Merged

Yaraslaut merged 1 commit into
feature/forms-object-array-slotfrom
feature/forms-prefill-from-json

Conversation

@Yaraslaut

Copy link
Copy Markdown
Member

Fixes #814. ⚠️ Stacked on #809 (base branch feature/forms-object-array-slot): it decodes into #809's itemFields / rows / fieldText slot contract. Retarget to master once #809 merges.

Editing flow: open saved record → read its DTO → form.prefillFromJson(json) → user edits → submit.

Member Wire value → draft text
Quantity {num,den,dp} → exact digits at canonical x-decimalPlaces, display locale; unit selector reset to canonical
plain number never exponent form; padded to x-displayDecimals when declared (#808), never rounded
integer / Choice / enum exact digits / valueJson (ids past 2^53 exact via JsonExact)
Timestamp ISO → wall clock in displayOffsetMinutes
std::vector<Row> rows of {member: cellText}, each decoded by the row member's descriptor
  • decodeFieldValue(field, value) is the inverse of encodeFieldText; prefill(values) replaces the whole draft, re-seeds every drawn control (prefillRevision) and slot (fieldText, rows), re-selects a fetched Choice when its options arrive (previously it showed "— select —" for a retained value), re-fetches dependent Choices.
  • Never submits: the final revalidate runs inside programmaticEdit. The first version reused withoutAutoSubmit and the round-trip test caught the prefilled form submitting itself — see the filed follow-up about the same pattern on tab rebuilds.
  • Spec: docs/spec/forms/forms.md, new "Prefill — loading a stored payload for editing".

Tests (MSVC 14.51, Qt 6.11.1): new tst_DynamicFormPrefill.qml (11 cases, round trip vs previewLine for every kind, de_DE, UTC+2, slots, fetched Choice, no submit). Removing the re-seed reddens 5; a wrong Quantity decoder reddens 8. forms_qml_logic 348 passed.

🤖 Generated with Claude Code

…record into DynamicForm for editing (fixes #814)

Every prefill path wrote *control text*: CollectionView sets a `field_<name>`
TextField, WizardView calls setFieldValue with text. An editing flow -- open a
saved sample, read its section DTO, prefill the form, edit, submit -- had to
turn each wire value into the text its control holds by hand: a {num,den,dp}
into locale digits at the declared precision, an ISO instant into the display
zone, a closed-set value into its valueJson, and every row of a
std::vector<Row> into cell texts, the inverse of the encoder #809 added.

`decodeFieldValue(field, value)` is that inverse, kind by kind, on exact
digits (a Quantity's num/den are divided as digit strings, an int64 id from
JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a
payload object, resets unit selectors to the canonical unit, bumps
`prefillRevision` so every drawn control re-seeds (a fetched Choice also
re-selects when its options arrive -- before this it showed "— select —" for a
retained value), re-fetches dependent Choices, and revalidates *inside* the
programmaticEdit window. That last point is measured, not assumed: the first
version reused withoutAutoSubmit, whose final revalidate runs after the
suppression is lifted, and the round-trip case caught the ready prefilled form
submitting itself. `prefillFromJson(text)` parses with JsonExact.

tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against
previewLine for every member kind, the drawn controls and slots holding the
values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a
fetched Choice with an id past 2^53, and no submission. Removing the re-seed
reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348
passed on MSVC 14.51 / Qt 6.11.1.

Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes
into).

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@codecov

codecov Bot commented Sep 24, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.

📢 Thoughts on this report? Let us know!

@Yaraslaut
Yaraslaut merged commit 2b1a564 into feature/forms-object-array-slot Sep 25, 2026
36 checks passed
Yaraslaut added a commit that referenced this pull request Sep 25, 2026
…record into DynamicForm for editing (fixes #814) (#817)

Every prefill path wrote *control text*: CollectionView sets a `field_<name>`
TextField, WizardView calls setFieldValue with text. An editing flow -- open a
saved sample, read its section DTO, prefill the form, edit, submit -- had to
turn each wire value into the text its control holds by hand: a {num,den,dp}
into locale digits at the declared precision, an ISO instant into the display
zone, a closed-set value into its valueJson, and every row of a
std::vector<Row> into cell texts, the inverse of the encoder #809 added.

`decodeFieldValue(field, value)` is that inverse, kind by kind, on exact
digits (a Quantity's num/den are divided as digit strings, an int64 id from
JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a
payload object, resets unit selectors to the canonical unit, bumps
`prefillRevision` so every drawn control re-seeds (a fetched Choice also
re-selects when its options arrive -- before this it showed "— select —" for a
retained value), re-fetches dependent Choices, and revalidates *inside* the
programmaticEdit window. That last point is measured, not assumed: the first
version reused withoutAutoSubmit, whose final revalidate runs after the
suppression is lifted, and the round-trip case caught the ready prefilled form
submitting itself. `prefillFromJson(text)` parses with JsonExact.

tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against
previewLine for every member kind, the drawn controls and slots holding the
values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a
fetched Choice with an id past 2^53, and no submission. Removing the re-seed
reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348
passed on MSVC 14.51 / Qt 6.11.1.

Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes
into).

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Yaraslaut added a commit that referenced this pull request Sep 25, 2026
…record into DynamicForm for editing (fixes #814) (#817)

Every prefill path wrote *control text*: CollectionView sets a `field_<name>`
TextField, WizardView calls setFieldValue with text. An editing flow -- open a
saved sample, read its section DTO, prefill the form, edit, submit -- had to
turn each wire value into the text its control holds by hand: a {num,den,dp}
into locale digits at the declared precision, an ISO instant into the display
zone, a closed-set value into its valueJson, and every row of a
std::vector<Row> into cell texts, the inverse of the encoder #809 added.

`decodeFieldValue(field, value)` is that inverse, kind by kind, on exact
digits (a Quantity's num/den are divided as digit strings, an int64 id from
JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a
payload object, resets unit selectors to the canonical unit, bumps
`prefillRevision` so every drawn control re-seeds (a fetched Choice also
re-selects when its options arrive -- before this it showed "— select —" for a
retained value), re-fetches dependent Choices, and revalidates *inside* the
programmaticEdit window. That last point is measured, not assumed: the first
version reused withoutAutoSubmit, whose final revalidate runs after the
suppression is lifted, and the round-trip case caught the ready prefilled form
submitting itself. `prefillFromJson(text)` parses with JsonExact.

tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against
previewLine for every member kind, the drawn controls and slots holding the
values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a
fetched Choice with an id past 2^53, and no submission. Removing the re-seed
reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348
passed on MSVC 14.51 / Qt 6.11.1.

Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes
into).

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Yaraslaut added a commit that referenced this pull request Sep 25, 2026
…record into DynamicForm for editing (fixes #814) (#817)

Every prefill path wrote *control text*: CollectionView sets a `field_<name>`
TextField, WizardView calls setFieldValue with text. An editing flow -- open a
saved sample, read its section DTO, prefill the form, edit, submit -- had to
turn each wire value into the text its control holds by hand: a {num,den,dp}
into locale digits at the declared precision, an ISO instant into the display
zone, a closed-set value into its valueJson, and every row of a
std::vector<Row> into cell texts, the inverse of the encoder #809 added.

`decodeFieldValue(field, value)` is that inverse, kind by kind, on exact
digits (a Quantity's num/den are divided as digit strings, an int64 id from
JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a
payload object, resets unit selectors to the canonical unit, bumps
`prefillRevision` so every drawn control re-seeds (a fetched Choice also
re-selects when its options arrive -- before this it showed "— select —" for a
retained value), re-fetches dependent Choices, and revalidates *inside* the
programmaticEdit window. That last point is measured, not assumed: the first
version reused withoutAutoSubmit, whose final revalidate runs after the
suppression is lifted, and the round-trip case caught the ready prefilled form
submitting itself. `prefillFromJson(text)` parses with JsonExact.

tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against
previewLine for every member kind, the drawn controls and slots holding the
values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a
fetched Choice with an id past 2^53, and no submission. Removing the re-seed
reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348
passed on MSVC 14.51 / Qt 6.11.1.

Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes
into).

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Yaraslaut added a commit that referenced this pull request Sep 25, 2026
…orm encodes its rows (#809)

* forms(qml): a host slot can draw a std::vector<Row> member, and the form encodes its rows

A collection of objects has no built-in control: the comma-separated array
control encodes strings, so DynamicForm reports the member unrepresentable and
a form that must carry it never becomes ready. That was right for the built-in
controls, and it left a host with nowhere to go -- registering a grid slot for
the member changed nothing, because the slot could only hand `setValue` a text
the array encoder then split on commas, and the slot was never told what the
row type's members are.

When a registered slot claims a top-level `std::vector<Row>` member:

  - `fields` describes the row type with the same descriptor builder the
    action uses (`describeObject`, depth-limited to one level so a
    self-referential row type cannot loop) and puts it on the field as
    `itemFields`: label, unit, decimals, readOnly, required and the kind
    flags, in x-order order -- the grid's columns. `isObjectArray` and
    `claimedBySlot` say which case the descriptor is.
  - `unrepresentable` is "" for it, because an encoding now exists.
  - The slot writes rows as `{member: cellText}` objects, where a cell holds
    what that member's built-in control would hold. `encodeObjectArray`
    encodes every cell with `encodeFieldText` -- `fieldJsonLiteral`'s body,
    lifted to take an explicit text and unit index -- so a `Quantity` cell is
    an exact {num,den,dp}, an over-precise one is refused rather than rounded,
    a blank optional cell is omitted and a blank required one keeps the form
    unready.

Slots also gain four optional members, each assigned only when the slot
declares it, so every existing slot is untouched: `fieldText` (a binding to the
retained text -- until now a slot never learned a value it had not written
itself, so prefill, `resetFields()` and a tab rebuild all left it stale),
`rows`, `setRows(rows)` and `form`. With `fieldText`, CollectionView's row
prefill reaches a slot-overridden scalar field too, which views.md listed as a
limitation.

Nothing changes without a slot: tst_DynamicFormNestedAggregate.qml is
untouched and green. The new tst_DynamicFormObjectArraySlot.qml (18 cases)
asserts submitted bodies; replacing the cell encoder with plain string quoting
reddens 3 of them. forms_qml_logic: 335 passed on MSVC 14.51 / Qt 6.11.1.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* docs(forms): the object-array suite has 18 cases, not 17

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* forms(qml): prefill(values) / prefillFromJson(text) -- load a stored record into DynamicForm for editing (fixes #814) (#817)

Every prefill path wrote *control text*: CollectionView sets a `field_<name>`
TextField, WizardView calls setFieldValue with text. An editing flow -- open a
saved sample, read its section DTO, prefill the form, edit, submit -- had to
turn each wire value into the text its control holds by hand: a {num,den,dp}
into locale digits at the declared precision, an ISO instant into the display
zone, a closed-set value into its valueJson, and every row of a
std::vector<Row> into cell texts, the inverse of the encoder #809 added.

`decodeFieldValue(field, value)` is that inverse, kind by kind, on exact
digits (a Quantity's num/den are divided as digit strings, an int64 id from
JsonExact.parse stays exact). `prefill(values)` replaces the whole draft from a
payload object, resets unit selectors to the canonical unit, bumps
`prefillRevision` so every drawn control re-seeds (a fetched Choice also
re-selects when its options arrive -- before this it showed "— select —" for a
retained value), re-fetches dependent Choices, and revalidates *inside* the
programmaticEdit window. That last point is measured, not assumed: the first
version reused withoutAutoSubmit, whose final revalidate runs after the
suppression is lifted, and the round-trip case caught the ready prefilled form
submitting itself. `prefillFromJson(text)` parses with JsonExact.

tst_DynamicFormPrefill.qml (11 cases) asserts the round trip against
previewLine for every member kind, the drawn controls and slots holding the
values, de_DE and a UTC+2 display zone, a later edit, replacement semantics, a
fetched Choice with an id past 2^53, and no submission. Removing the re-seed
reddens 5 cases; a wrong Quantity decoder reddens 8. forms_qml_logic: 348
passed on MSVC 14.51 / Qt 6.11.1.

Stacked on #809 (the rows / fieldText slot contract and itemFields it decodes
into).

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>

* forms(qml): a slot registered by kind claims an object-array collection too

slotClaims() is the delegate's own slot resolution, but it passed five
arguments while the delegate passes the kind as well, so a grid registered
with SlotRegistry.byKind("objectArray", ...) was drawn while the form kept
the collection unrepresentable and never encoded its rows. The kind is now
computed once per descriptor and handed to both.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y4eif7wQNNhSkHUKYqq5Xq

* forms(qml): prefill() suppresses auto-submit through withoutAutoSubmit

The helper now revalidates inside its suppression window, so the hand-rolled
copy in prefill() and its comment explaining why it avoided the helper no
longer describe anything.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Y4eif7wQNNhSkHUKYqq5Xq

---------

Co-authored-by: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant