Skip to content

feat(pages): write DynamicClasses and DynamicCellClass as expressions (#750) - #682

Merged
ako merged 1 commit into
mainfrom
claude/nice-einstein-a74lz4
Sep 25, 2026
Merged

ako merged 1 commit into
mainfrom
claude/nice-einstein-a74lz4

Conversation

@ako

@ako ako commented Sep 25, 2026

Copy link
Copy Markdown
Owner

Relates to mendixlabs#750. This is slice 2 of docs/11-proposals/PROPOSAL_first_class_expressions.md, the named-property step. Q4 was decided on 2026-09-25: schema-driven in the end, shipped named properties first.

What changes

A widget's DynamicClasses and a datagrid column's DynamicCellClass hold one Mendix expression, and MDL now writes it as-is:

container c1 (dynamicclasses: if $currentObject/Featured then 'is-featured' else '') { }
container c2 (dynamicclasses: 'is-featured') { }       -- the string: the class is-featured
column Name (attribute: Name, DynamicCellClass: if $currentObject/Price > 100 then 'highlight' else '')
alter page M.P { set DynamicClasses = if $currentObject/Featured then 'a' else 'b' on c1 };

A quoted value is a Mendix string, the same rule the OData client's credentials follow since #676. The doubled-quote spelling 'if … then ''a'' else ''''' is gone.

How

  • Grammar: widgetPropertyV3 and alterPageAssignment accept expression last, so every existing value form keeps its parse.
  • Visitor:
    • The two properties store the source text of whichever value alternative matched. A $x/Y + … can be claimed by the datasource or action alternative, so the visitor doesn't rely on which alternative matched.
    • A bracketed list still goes to MDL-WIDGET27/32.
    • An expression on any other widget property, or in any other alter … set, is an error, not a silent empty value.
  • describe: prints the stored expression as-is.
  • MDL-WIDGET33: refuses the old spelling on create and alter. That is a quoted string whose content looks like expression text: a $, a quote character, or a leading if. It would now store that text as a class name, so the error's suggestion is the unquoted expression. Plain class names like 'btn btn-lg' pass.
  • Messages: MDL-WIDGET32 and the ALTER setter's bracketed-list error now point at the unbracketed expression.

Evidence

Tests were written against the pre-change behavior. Controls: class-name strings, class lists, the unquoted expression and visible: [...] pass throughout.

Measured on a copy of ako/TestApp (Mendix 11.14.0):

  • Appearance.DynamicClasses stores if $currentObject/Featured then 'is-featured' else '' and 'plain-class', and columnClass stores its expression.
  • describe prints the same MDL, and describe output is byte-identical after a describe → exec.
  • alter … set with the expression stores it. With the old spelling it is refused, and nothing is written.

Migration

28 uses were converted: four skills, the docs site, the MDL quick reference, the syntax help, and seven example / bug-test files.

  • A script lexed each MDL string and rewrote only values whose content is expression text. I reviewed the dry run, and every converted .mdl still passes check.
  • Skill prose that told people to double the quotes is updated. One alter-page example had become self-contradictory after conversion and was rewritten.
  • Proposals and the changelog keep the old spelling as history.

New regression file: mdl-examples/bug-tests/750-dynamicclasses-legacy-quoted-expression.fail.mdl.

Not in this PR

  • Other schema-typed expression properties (a column's Visible, among others) keep the quoted form until the schema-driven step. The alter-page skill says so.
  • mx check was not run. There is no mxbuild in this environment.

Checks

make build, make test, make lint, make check-mdl and make check-findings pass on current main.

🤖 Generated with Claude Code

https://claude.ai/code/session_01P65SqmwwvbWdJVRwhYiMQw


Generated by Claude Code

PROPOSAL_first_class_expressions.md slice 2, named properties (Q4
decided: schema-driven, shipped named-properties first). A widget's
DynamicClasses and a datagrid column's DynamicCellClass hold one Mendix
expression, and MDL now writes it as-is:

  dynamicclasses: if $currentObject/Featured then 'is-featured' else ''
  dynamicclasses: 'is-featured'      -- the string: the class is-featured

A quoted value is a Mendix string - the rule the OData client's
credentials follow - so the doubled-quote spelling is gone.

- grammar: widgetPropertyV3 and alterPageAssignment accept `expression`
  LAST, so every existing value form keeps its parse.
- visitor: the two properties store the source text of whichever value
  alternative matched (a `$x/Y + ...` can be claimed by the datasource or
  action alternative). A bracketed list still reaches MDL-WIDGET27/32.
  An expression on any other widget property, or in any other ALTER SET,
  is an error, so the wider rule cannot open a silent empty value.
- describe prints the stored expression as-is.
- MDL-WIDGET33 refuses the old spelling - a quoted string whose content
  looks like expression text ($, a quote, or a leading `if`) - on create
  and alter; it would now store that text as a class name. The
  suggestion is the unquoted expression.
- MDL-WIDGET32 and the ALTER setter's bracketed-list error now point at
  the unbracketed expression instead of the quoted form.

Measured on a copy of ako/TestApp (11.14.0): Appearance.DynamicClasses
stores `if $currentObject/Featured then 'is-featured' else ''` and
`'plain-class'`, columnClass stores its expression; describe prints the
same MDL; describe -> exec leaves describe byte-identical; ALTER with
the expression stores it; ALTER with the old spelling is refused with
nothing written.

The 28 uses in skills, docs-site, the quick reference, the syntax help,
examples and bug tests are migrated by lexing each MDL string and
rewriting only values whose content is expression text; proposals and
the changelog keep the old spelling as history. A column's pluggable
`Visible` keeps the quoted form until the schema-driven step, and the
alter-page skill says so.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01P65SqmwwvbWdJVRwhYiMQw
@ako
ako merged commit 68535d0 into main Sep 25, 2026
17 checks passed
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.

2 participants