Class ModalDef
- All Implemented Interfaces:
Widget
present=dialog row action opens — the payload the
modal-open confirmation fetch returns.
A widget contract driven by a view-module runtime
This bean is a general widget contract and lives in this module, but the browser runtime that paints it ships with
the rich-view module (it is part of that module's juneau-views.js monolith, which is not split apart here).
So a page that opens one of these dialogs loads the view runtime; nothing in this module reaches back to it.
Safe-by-construction confirmation body
The confirmation body is typed structured fields (fields: incident #, title, service, current
status — the confirmation field set), server-rendered against the current, authoritative record and
bound to the target identifier. The juneau-views.js runtime paints each field's value with
textContent — NEVER innerHTML, never raw markup, never the non-table panel-content sink.
Live/remote data is attacker-influenceable (anyone who can title an incident or post in a watched thread) and this
origin holds the CSRF token, so an HTML sink here would be stored-XSS → token-theft →
arbitrary-write; typed fields painted with textContent close that off by construction.
The idempotency key rides here
idempotencyKey is a server-minted key value, minted at modal-open and bound to
(action, targetId). The runtime carries it verbatim on the submit so a double-click / re-submit / browser
retry all collapse to one effect; the server checks that binding on submit and answers a mismatch with a named
refusal, never a replayed success. The minting/binding helper itself is a table-and-row-action concern and stays
in the rich-view module, which is why this field is a plain String rather than that helper's type.
selfTargeted — the opt-in for a key that targets itself
A dialog opened where there is no row (a ribbon-hosted dialog whose submit creates the thing it is about)
has no artifact id to bind its key to at modal-open. The rich-view module's key helper can mint a key whose
bound target is its own value; selfTargeted is the signal that tells the runtime to send that
value as the submitted target instead of the row's id.
The signal has to ride on this bean, because it cannot be inferred from the wire: idempotencyKey is an
opaque string either way, indistinguishable in shape from an artifact-bound key's value. It is therefore an
explicit, additive, per-dialog opt-in — not a blanket runtime precedence rule. A dialog that leaves
this unset behaves exactly as it does today, including one that sets idempotencyKey for an
artifact-bound reason: it keeps sending the row's real id, which is what its own mint call bound.
Set it only on a modal whose key came from the self-targeted mint helper. Setting it on a modal carrying an artifact-bound key silently discards the real target and makes the server's binding check vacuous.
keepOpenOnSubmit — the opt-in for an in-dialog result receipt
By default the runtime closes a dialog at confirm-click time and then submits, so every dialog is gone before its
result exists. keepOpenOnSubmit suppresses that close, and the runtime instead paints the write's
result into the dialog that is already open — a receipt.
A receipt host is non-submittable by construction, not by convention: the runtime paints it with a distinct
function that creates no confirm control at all, and it refuses to paint one whose payload carries a
form. It is reached only after a committed write (an
ActionResult whose outcome is success), so a result host can never appear for a write that did not
happen. A follow-up read-only form source is named by that result's resultForm.
The opt-in has to live here rather than on the action bean, because the confirm handler must decide whether to
close before any response exists. A dialog synthesized from a confirm string alone (no form-source
URL) therefore cannot opt in — a receipt-bearing action must serve a real ModalDef.
Additive, and named degradation, accepted: an older runtime ignoring this flag closes the dialog and drops the receipt. The write still happens; the receipt is simply not shown.
childActions — the dialog-scoped child-action catalog
A type="action" form input names an action id, and the runtime resolves it against the enclosing view's
row-action catalog. A stacked step reached from inside an already-open dialog (a Review step in a wizard)
has nothing legal to resolve against there: it is not a row action, and publishing it as one would surface it in
every row's action menu. childActions is that missing catalog, scoped to one open dialog.
Scoping is structural, not a flag some menu builder has to remember to filter on: this catalog rides on a per-open payload, so the row-action menu and the ribbon resolver never see it and there is no exclusion logic to rot. Resolution precedence is likewise fail-safe: the existing row-action check runs first and unchanged, so a served payload can never shadow — and thereby bypass the gating of — a declared row action.
chrome and steps — dialog styling and a built-in wizard
chrome names a dialog chrome the page script registered with the runtime; the runtime hands that chrome the
freshly built dialog parts so it can restyle them. An unregistered name is ignored and the default chrome is kept.
steps turns the dialog into a wizard: the runtime shows one ModalDef.Step at a time with Back and Next
buttons, and the last step's button is the dialog's own confirm. Both are additive-only and omitted from the wire
when unset, so neither bumps CONTRACT_VERSION.
Not a new row-action wire field
This is the response a row action's form-source URL returns, not a new field on the frozen row-action wire
schema: the modal's declaration lives in this separately-served payload so the row-action contract stays frozen.
The same reasoning is why keepOpenOnSubmit and childActions land here rather than on
RowAction / RibbonAction, which stay frozen and untouched.
Example:
ModalDef
See Also:
The third named bar-slot host
barSlot is the third named BarSlot attachment — alongside the rich-view module's page
and row-detail hosts (that module never depends on this one, so they are not linked here). Unlike those two
— Java-only fields a server-side emitter paints directly, omitted from their wire — this modal itself
is the wire payload the modal-open confirmation fetch returns, with no separate server-rendered pass the
dialog could ride into. So this field is carried on the wire (additive-only: null when unset, and
its presence does not bump CONTRACT_VERSION), and the juneau-views.js runtime paints the region
and its dynamic-count sidecar client-side from the JSON, the same way it already paints typed confirmation
fields and form controls — textContent only, never innerHTML. The dialog
anchors its own region immediately after the title (the ANCHOR_DIALOG_TITLE
placement in juneau-views.js), owning that placement itself rather than leaning on the shared strip builder,
exactly as the row-detail host owns its own ribbon-trailing relocation.
- Since:
- 10.0.0
-
Nested Class Summary
Nested ClassesModifier and TypeClassDescriptionstatic classOne entry in a dialog'schildActionscatalog — a stacked step atype="action"input inside this dialog's form may open.static classA single typed confirmation field: a label and a value, painted client-side withtextContent(neverinnerHTML).static classOne page of astepswizard: an id, an optional title and confirm-button label, and the typed display fields painted for that page. -
Field Summary
FieldsModifier and TypeFieldDescriptionOptional additive bar slot anchored to this dialog's title — the third namedBarSlotattachment (see the class Javadoc for how this host differs from the other two).The dialog-scoped catalog of stacked child actions atype="action"input informmay open (see the class Javadoc); omitted from the wire when unset or empty.The name of a dialog chrome the page script registered with the runtime, which restyles this dialog's parts (see the class Javadoc); omitted from the wire when unset (the default chrome).static final StringThe frozen modal contract version.The frozen modal contract version discriminator.static final StringTheModalDef.Field.kindtoken for a monospaced, copyable value (an id, a URL, a generated token).static final StringTheModalDef.Field.kindtoken for an ordinary text value — the default when the field declares none.The typed confirmation fields, in display order; omitted from the wire when none are declared.The optional input form; omitted from the wire when unset (a confirm-only modal).The server-minted idempotency key value the runtime carries on the submit; omitted from the wire when unset.Whether the runtime must not close this dialog at confirm-click time, so the write's result can be painted into it as a receipt (see the class Javadoc); omitted from the wire when unset orfalse .WhetheridempotencyKeyis a self-targeted key whose own value the runtime must send as the submitted target (see the class Javadoc); omitted from the wire when unset orfalse .The wizard steps the runtime shows one at a time with Back and Next (see the class Javadoc); omitted from the wire when unset or empty.The modal title / confirmation prompt. -
Constructor Summary
Constructors -
Method Summary
Modifier and TypeMethodDescriptionDeclares the additive bar slot anchored to this dialog's title.checked()The serving-path hook every app@RestGetthat returns aModalDefshould invoke.childAction(ModalDef.ChildAction value) Adds one entry to this dialog's child-action catalog.Selects the dialog chrome the page script registered under the given name.Adds one typed confirmation field displayed as monospaced, copyablecode.static ModalDefStarts a newModalDefwith the specified title / confirmation prompt.Adds one typed confirmation field.Sets the optional input form.idempotencyKey(String value) Sets the server-minted idempotency key value carried on the submit.keepOpenOnSubmit(boolean value) Declares that the runtime must keep this dialog open across its submit so the result can be painted into it as a receipt (see the class Javadoc).selfTargeted(boolean value) Declares thatidempotencyKeyis a self-targeted key — the runtime sends the key's own value as the submitted target rather than a row id.step(ModalDef.Step value) Adds one wizard step.voidvalidate()Fail-closed bean validation.
-
Field Details
-
CONTRACT_VERSION
The frozen modal contract version. Bumped only on a breaking wire change to this modal contract.Moves in lockstep with
FormDef.CONTRACT_VERSIONand the runtime's baked-in literal: the runtime compares one literal against both this version and the nested form's, so bumping any two of the three makes every form-bearing dialog refuse to open.- See Also:
-
FIELD_KIND_TEXT
TheModalDef.Field.kindtoken for an ordinary text value — the default when the field declares none.- See Also:
-
FIELD_KIND_CODE
TheModalDef.Field.kindtoken for a monospaced, copyable value (an id, a URL, a generated token).- See Also:
-
contractVersion
The frozen modal contract version discriminator.Defaults to
CONTRACT_VERSIONon every modal, confirm-only or form-bearing, from the instant it is constructed — the guarantee does not depend onchecked()being called. Guaranteed present is not the same as guaranteed valid: presence is this field's whole job, structural well-formedness isvalidate()'s (seechecked()). -
title
The modal title / confirmation prompt. -
fields
The typed confirmation fields, in display order; omitted from the wire when none are declared. -
form
The optional input form; omitted from the wire when unset (a confirm-only modal). -
idempotencyKey
The server-minted idempotency key value the runtime carries on the submit; omitted from the wire when unset. The value is minted (and its(action, targetId)binding re-checked on submit) by the rich-view module's idempotency-key helper. -
selfTargeted
WhetheridempotencyKeyis a self-targeted key whose own value the runtime must send as the submitted target (see the class Javadoc); omitted from the wire when unset orfalse .Additive-only: like
barSlot, its presence does not bumpCONTRACT_VERSION. -
barSlot
-
keepOpenOnSubmit
Whether the runtime must not close this dialog at confirm-click time, so the write's result can be painted into it as a receipt (see the class Javadoc); omitted from the wire when unset orfalse .Additive-only: like
barSlotandselfTargeted, its presence does not bumpCONTRACT_VERSION. -
childActions
The dialog-scoped catalog of stacked child actions atype="action"input informmay open (see the class Javadoc); omitted from the wire when unset or empty. -
chrome
The name of a dialog chrome the page script registered with the runtime, which restyles this dialog's parts (see the class Javadoc); omitted from the wire when unset (the default chrome).Additive-only: like
barSlot, its presence does not bumpCONTRACT_VERSION. -
steps
The wizard steps the runtime shows one at a time with Back and Next (see the class Javadoc); omitted from the wire when unset or empty.Additive-only: like
barSlot, its presence does not bumpCONTRACT_VERSION.
-
-
Constructor Details
-
ModalDef
public ModalDef()
-
-
Method Details
-
create
Starts a newModalDefwith the specified title / confirmation prompt.- Parameters:
title- The modal title / confirmation prompt. Must not benull or blank.- Returns:
- A new mutable
ModalDefto chain builder calls on. - Throws:
IllegalArgumentException- Iftitleisnull or blank.
-
field
Adds one typed confirmation field.- Parameters:
label- The field label. Must not benull or blank.value- The field value (read back from the current record). Can benull .- Returns:
- This object.
-
codeField
Adds one typed confirmation field displayed as monospaced, copyablecode.- Parameters:
label- The field label. Must not benull or blank.value- The field value. Can benull .- Returns:
- This object.
-
childAction
Adds one entry to this dialog's child-action catalog.- Parameters:
value- The child action. Must not benull .- Returns:
- This object.
- Throws:
IllegalArgumentException- Ifvalueisnull .
-
chrome
Selects the dialog chrome the page script registered under the given name.- Parameters:
value- The chrome name. Can benull to unset (the default chrome); a non-null value must not be blank.- Returns:
- This object.
-
step
Adds one wizard step.- Parameters:
value- The step. Must not benull .- Returns:
- This object.
- Throws:
IllegalArgumentException- Ifvalueisnull .
-
keepOpenOnSubmit
Declares that the runtime must keep this dialog open across its submit so the result can be painted into it as a receipt (see the class Javadoc).Passing
false clears the flag rather than serializing an explicitfalse: the wire carries this opt-in only when it is on, so an un-opted-in dialog's payload is byte-identical to one that never set it.- Parameters:
value-true to opt in;false (or unset) keeps the default close-then-submit behaviour.- Returns:
- This object.
-
form
Sets the optional input form.- Parameters:
value- The form. Can benull to unset (a confirm-only modal).- Returns:
- This object.
-
idempotencyKey
Sets the server-minted idempotency key value carried on the submit.- Parameters:
value- The key value. Can benull to unset.- Returns:
- This object.
-
selfTargeted
Declares thatidempotencyKeyis a self-targeted key — the runtime sends the key's own value as the submitted target rather than a row id.Set this only alongside a key minted by the rich-view module's self-targeted mint helper; pairing it with an artifact-bound key discards that key's real target. See the class Javadoc.
Passing
false clears the flag rather than serializing an explicitfalse: the wire carries this opt-in only when it is on, so an un-opted-in dialog's payload is byte-identical to one that never set it.- Parameters:
value-true to opt in;false (or unset) keeps the default row-id behaviour.- Returns:
- This object.
-
barSlot
Declares the additive bar slot anchored to this dialog's title.See
barSlot— a third named host for the sameBarSlotbean, not a re-use of the rich-view module's page or row-detail hosts.- Parameters:
value- The bar slot. Can benull (no dialog bar slot).- Returns:
- This object.
-
validate
Fail-closed bean validation.Requires a non-blank
titleand a non-blank label on eachModalDef.Field; when aformis present it delegates toFormDef.validate(). Does not requirecontractVersionto be set toCONTRACT_VERSION(or set at all) — a modal whose version was explicitly cleared, or that arrived from a foreign producer that never set one, must not false-refuse on that alone. Version-presence and structural well-formedness are deliberately independent properties (design note, §5.4).Also rejects, fail-closed at serve time rather than silently on the wire: a
ModalDef.Field.kindoutside thetext/codeallowlist; a blank, duplicate or unlabelledModalDef.ChildActionid;ModalDef.ChildAction.carryDraftson a modal that declares noform(nothing to collect from); andModalDef.ChildAction.carryDraftson a child that declares noModalDef.ChildAction.form(nowhere to put them — the runtime would take its confirm-only branch, issue no GET, and the drafts would vanish silently).- Specified by:
validatein interfaceWidget- Throws:
IllegalArgumentException- If this modal is not well-formed.
-
checked
The serving-path hook every app@RestGetthat returns aModalDefshould invoke.contractVersionis guaranteed present on every modal from construction, so this is no longer required for versioning. It stays strongly recommended for one remaining duty and is redundant-but-harmless for another:- Non-REST producers (a direct
Json.of(...)serialization, or any consumer marshalling a modal outside a REST response) have no other gate: calling this is the only fail-closed structuralvalidationthey get, and skipping it means a malformed modal/form serializes as contract-valid with no complaint. - REST responses are validated independently:
WidgetsMixin.WidgetValidationProcessorcallsvalidate()on every REST response that is aWidget, fail-closed with a 500, whether or not the producing endpoint called this method. Calling it anyway is safe (validation is idempotent) and remains the only gate for the non-REST case above.
formis present it also re-stampsCONTRACT_VERSIONon the form and recurses intoFormDef.checked().- Returns:
- This object.
- Throws:
IllegalArgumentException- If this modal (or its form) is not well-formed.
- Non-REST producers (a direct
-