Skip to content
This repository was archived by the owner on Nov 4, 2026. It is now read-only.
This repository was archived by the owner on Nov 4, 2026. It is now read-only.

Form-view FormField.visibleOn (CEL) is never evaluated — conditional fields always render #2212

Closed

Description

@baozhoutao

Symptom

A form view section field with a CEL `visibleOn` predicate is always visible. Showcase repro (framework `examples/app-showcase/src/views/task.view.ts`):

```ts
{ field: 'notes', visibleOn: Precord.priority == 'urgent', colSpan: 2 }
```

The server meta correctly ships `"visibleOn": {"dialect":"cel","source":"record.priority == 'urgent'"}` (verified via `GET /api/v1/meta/view/showcase_task` → `.formViews.default.sections[0].fields[6]`), but the notes field renders even when priority = Low. Field-level `visibleWhen`/`readonlyWhen`/`requiredWhen` all work — only the view-level `FormField.visibleOn` is dead.

Root cause — the chain is broken in three places

Console edit path: `AppContent` → `ModalForm` (+ `resolveFormViewLayout` sections) → `buildSectionFields` (`packages/plugin-form/src/sectionFields.ts`) → SchemaRenderer `type:'form'` → `packages/components/src/renderers/form/form.tsx`.

  1. Object shape silently dropped — `sectionFields.ts` `attachVisibility()` (line 45) and `ObjectForm.tsx` `normalizeVisibility()` (line 411) only accept `typeof expr === 'string'`. The spec `P`-template wire shape is `{dialect:'cel', source}` → predicate never attached.
  2. The evaluator isn't CEL — even for a string expr, the attached `visible(formData)` calls `evaluateCondition()` (`packages/fields/src/index.tsx:1833`), which only understands legacy `{field, operator, value}` objects and returns `true` for any unknown format ("Default to true").
  3. Nothing consumes the predicate anyway — no renderer in the form chain ever calls `FormField.visible(formData)`; `form.tsx:538` destructures `visibleOn` out of the field config and drops it on the floor.

Meanwhile the working field-level rules go through the canonical engine: `resolveFieldRuleState`/`evalFieldPredicate` (`packages/core/src/evaluator/fieldRules.ts`), which already accepts `string | {dialect, source}` and binds `record` scope via `@objectstack/formula` ExpressionEngine (ADR-0036: no private expression DSL).

Fix direction

Route view-level `FormField.visibleOn` through the same canonical engine:

  • `form.tsx`: evaluate `visibleOn` with `evalFieldPredicate` against the live `ruleRecord` (fail-open), both at field render and in the stale-error-clearing effect.
  • `sectionFields.ts`: carry `visibleOn` (string or `{dialect, source}`) through `normalizeSectionField` instead of attaching a dead `visible()` closure; also carry field-level `visibleWhen`/`readonlyWhen`/`requiredWhen` from object-schema metadata so sectioned forms honor them like flat forms do.
  • `types/form.ts`: widen `FormField.visibleOn` to the spec Expression shape.

Found while dogfooding the framework app-showcase (list/form capability audit, P0-16).

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions