Skip to content

Two widget property-binding gaps found while building a data-bound catalogue page: (1) the Image widget's imageUrl/alternativeText texttemplate contentparams are silently dropped on write, and (2) ACTIONBUTTON's real Editability/ConditionalEditabilitySettings properties are not wired through editable: at all #928

Description

@TOFBuck

Summary

Found live while building eShop.Catalog_Overview for a phase-1 catalogue plan (a gallery of
product cards with a per-row image, and paging buttons that should grey out at the first/last
page). mxcli v0.18.0 (2026-08-14T20:29:13Z), devcontainer (linux/arm64), Mendix 11.12.2 project.

Both bugs were confirmed with minimal, isolated repros against a scratch page, and both were
cross-checked with mxcli bson dump against the real, underlying Mendix widget schema — the
properties genuinely exist in the Mendix model; mxcli's MDL writer just does not populate them
for these two cases.

Bug 1 — Image widget's imageUrl texttemplate contentparams binding is accepted at check

time and silently dropped on write

create-page.md's own documented idiom for binding a per-row image path is:

pluggablewidget 'com.mendix.widget.web.image.Image' cardImage (
  datasource: imageUrl,
  imageUrl: '{1}', contentparams: [{1} = PictureUrl]
)

mxcli check --references accepts this with no warning. mxcli exec accepts it too, but the
round-tripped describe page shows the placeholder with no bound parameter at all:

$ mxcli -p app.mpr -c "DESCRIBE PAGE MyModule.ProbePage"
...
image cardImage (ImageType: imageUrl, ImageUrl: '{1}')

ContentParams is simply absent from the output — not [{1} = PictureUrl], nothing. Building the
project then fails:

[error] [CE0720] "Place holder index 1 is greater than 0, the number of parameter(s)." at Image 'cardImage'

What was tried (all reproduce identically)

  • The documented numeric-placeholder form above (imageUrl: '{1}', contentparams: [{1} = X]), in
    every property order (contentparams before/after imageUrl:).
  • The {AttrName} shorthand form mentioned in custom-widgets.md's MCP-authoring section
    (imageUrl: '{PictureUrl}') — describe shows it did convert the placeholder to {1} form,
    but still drops the binding.
  • A bare, unquoted attribute reference (imageUrl: PictureUrl) — silently serialized as the
    literal string 'PictureUrl', not an attribute reference.
  • A bracket-expression form (imageUrl: [PictureUrl]) — the whole property is dropped, not just
    the binding.
  • A generic Attribute: PictureUrl key alongside datasource: imageUrl — also dropped entirely.
  • --engine legacy on mxcli exec — identical result.
  • Both a non-persistent view entity and a persistent entity as the datasource — identical
    result either way, ruling out entity persistence as a factor.
  • A control case in the same page/gallery: dynamictext debugText (content: 'x: {1}', contentparams: [{1} = PictureUrl]) — this one round-trips correctly
    (Content: 'x: {1}', ContentParams: [{1} = PictureUrl]), confirming the general
    attribute-resolution and contentparams mechanism works; the gap is specific to this
    PLUGGABLEWIDGET's texttemplate properties, not attribute binding in general.

custom-widgets.md's own auto-mapping table for third-party widget extraction independently
documents textTemplate as *skipped* -- too complex for auto-mapping, which is consistent with
this being a known-rough edge of the writer, not a one-off.

Impact

Any page using the Image widget's imageUrl (or, by the same mechanism, alternativeText) with a
per-row/per-object dynamic path — the standard way to show a different picture per list item —
cannot build. The failure is loud (CE0720, not silent), so at least it is caught by mxcli check

  • a build, rather than shipping a blank <img>.

Workaround used

None available without an architecture change (the documented fallback — datasource: image with
a System.Image specialization — requires re-seeding every image as a binary object, which was
out of proportion for a page-authoring task). Shipped with a static literal imageUrl instead
(every row shows the same fallback image), recorded as a known stub in the plan's SUMMARY.

Bug 2 — ACTIONBUTTON's Editability / ConditionalEditabilitySettings are not wired through

editable: (or any other property name tried)

Mendix's real Forms$ActionButton widget carries genuine Editability (enum: Always / …) and
ConditionalEditabilitySettings (the expression) properties — confirmed via:

$ mxcli bson dump -p app.mpr --type page --object "MyModule.ProbePage" --format json | grep -A3 Editab
{
  "Key": "Editability",
  "Value": "Always"
},
...
{
  "Key": "ConditionalEditabilitySettings",
  "Value": null
}

Writing editable: [SomeBooleanAttribute] on an actionbutton is accepted by mxcli check with
no MDL-WIDGET07 warning (i.e. mxcli recognizes editable: as a real, modeled concept), and
mxcli exec runs without error — but a bson dump immediately afterward shows Editability is
still "Always" and ConditionalEditabilitySettings is still null. The value has nowhere to go.

What was tried

Roughly a dozen candidate property names on actionbutton (active, enabled,
isvisible, editability, activewhen, activecondition, activeexpression,
enabledexpression, clickable, isenabled, isactive, conditionalenabled, disable,
disableexpression) — every one of these other than editable produces an explicit
MDL-WIDGET07 "... is not recognized and will be silently dropped on write" warning at check
time, which is the expected/documented behavior for a genuinely unknown property name. Only
editable is silently accepted as "known" yet has no effect on Forms$ActionButton's
Editability/ConditionalEditabilitySettings fields on write.

Impact

There is currently no way, via MDL, to bind an ACTIONBUTTON's active/inactive state to an
expression (the common "grey out Previous on the first page" pattern). Because MDL-WIDGET07
does not fire for editable: specifically, this fails silently — the property is accepted,
the build succeeds, and the button simply never becomes inactive, which is a worse failure mode
than an unrecognized-property warning would be.

Workaround used

A CSS-only substitute: dynamicclasses (a plain expression property, unaffected by this gap) adds
a .paging-inert class (opacity: 0.5; pointer-events: none;) when the flag is false. This
achieves the same visual/interaction outcome (greyed out, unclickable) without engaging Mendix's
native Editability, so an automated check for the literal HTML disabled attribute will not find
one — a check for the CSS class / computed pointer-events is needed instead.

Suggested fix direction

For Bug 1: either wire contentparams through for textTemplate-typed PLUGGABLEWIDGET properties
generally (matching how content/contentparams already works for native DYNAMICTEXT), or
document the gap explicitly in create-page.md's Image-widget section so the next reader does not
repeat this investigation.

For Bug 2: add a real editable:Editability/ConditionalEditabilitySettings mapping for
actionbutton, or — at minimum — make MDL-WIDGET07 fire for editable: on widget types where it
has no effect, so the gap is loud instead of silent.

Related (not duplicates)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions