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.

Create dialog ignores a field's defaultValue — a required select with a default renders empty and forces a manual choice #4047

Closed

Description

@os-zhuang

Migrated from objectstack-ai/objectstack#5150 under the file-at-destination ruling (objectstack-ai/objectstack#7167, maintainer 2026-08-10). Originally filed 2026-08-04T06:27:24Z. The full prior thread — including triage rulings and hold/restart conditions — remains on the source issue and MUST be read before acting on this card.


Summary

A field that declares defaultValue and required: true renders in the create dialog with no value selected and a required marker, so the user is forced to pick something the system already knows. The default is served to the console; the create form just does not apply it.

Measured 2026-08-04, @objectstack/console shipped with 17.0.0-rc.1, zh-CN console, fresh database, object create dialog from an object list page.

Repro

Field declaration:

status: Field.select({
  label: 'Status',
  required: true,
  defaultValue: 'draft',
  options: [
    { label: 'Draft',            value: 'draft',    default: true },
    { label: 'Pending Approval', value: 'pending'  },
    { label: 'Approved',         value: 'approved' },
    { label: 'Rejected',         value: 'rejected' },
  ],
}),

Note both spellings are present — the option's default: true and the field's defaultValue.

The default reaches the console intact. GET /api/v1/meta/objects/crm_customer_rating:

"status": {
  "label": "Status", "type": "select", "required": true,
  "defaultValue": "draft",
  "options": [{ "label": "Draft", "value": "draft", "default": true }, "..."]
}

Expected: the create dialog opens with Draft preselected.
Actual: the select shows its placeholder (请选择 / Select…) with a required asterisk. Nothing is preselected; the form cannot be submitted until the user opens the dropdown and picks a value by hand.

The server side is fine — a create that omits the field entirely is accepted and stored as draft:

$ curl --noproxy '*' -b cookies.txt -H 'Origin: http://localhost:4001' \
    -X POST http://localhost:4001/api/v1/data/crm_customer_rating \
    -H 'Content-Type: application/json' \
    -d '{"crm_account":"...","rating_year":2026}'
{"record":{"...":"...","status":"draft"}}

So this is purely the create form not seeding its default values from the object schema.

Why it matters beyond ergonomics

For a status-like field the forced choice is not neutral — every option is one click away and some of them have side effects. In our app choosing Approved at creation time immediately publishes a grade onto the parent customer record from a scorecard with no facts entered yet. The safe value is the declared default; the UI just refuses to use it.

More generally: defaultValue + required is a normal, correct way to model "this has a sensible starting value". Right now that combination produces the worst create-form experience of any modelling choice — strictly worse than declaring no default at all.

Note

There is code in plugin-form-*.js that builds default values from the object schema —

Object.keys(l.fields).forEach(t => {
  let n = l.fields[t];
  n.defaultValue !== void 0 && (e[t] = n.defaultValue);
});

— but the object create dialog does not appear to go through it. If that is a component-wiring gap rather than a missing feature, it may be a small fix.

Workaround we shipped

We removed the field from the create form entirely (via a visibleWhen predicate keyed on the record's autonumber being empty) and let the server apply the default. That works, but it is a workaround for the form not honouring metadata it already has — and it only works because conditional visibility happens to be usable here; see objectstack-ai/objectstack#5149 for the ways it is not.

Activity

  1. self-assigned this
    on Aug 10, 2026
  2. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Contributor

    Claim: PM loop round 2 (objectui whole-repo seat — queue fill per the maintainer's 2026-08-10 five-concurrent directive)
    Session: session_017Qqyix2QcnpUC9XeYVDzx3
    Branch: claude/issue-4047-create-dialog-defaults
    Worktree: objectui-issue-4047
    Domain: repo:objectui (whole-repo seat)
    File surface: the object create dialog's form-seeding path — expected around packages/plugin-form/src/ObjectForm.tsx (default seeding exists at :825 on origin/main) and/or the app-shell create-dialog wiring that bypasses it; plus tests. ⛔ Region-exclusive vs the in-flight #3569 branch (baozhoutao): do not touch GridField/grid-column files in packages/fields / packages/plugin-form. If the true producer of the defect is in another package than expected, report and fix at the producer (state where and why in the PR body) — do not patch a consumer.
    Container & model: M, mode:subagent, model: opus
    Serial constraints cleared: no open claim or PR on the create-dialog path; #3569's pushed branches touch GridField.tsx + showcase fixtures (disjoint files, region declared above).

    Premise re-verified at dispatch: seeding logic exists (ObjectForm.tsx:825 reads field.defaultValue); the card's server-side reading (create-omitting-field stores the default) makes this a console-only wiring defect, per the source triage (objectstack#5150).


    Generated by Claude Code

  3. yinlianghui commented on Aug 10, 2026

    @yinlianghui
    Contributor

    ACCEPT (objectui seat PM, session session_017Qqyix2QcnpUC9XeYVDzx3, review of record) — PR #4068.

    What shipped: create forms across all six object-form containers now open with the schema's declared static defaultValues, via one shared schemaDefaults module. Three boundaries measured and pinned in both directions: create-only (fixing a latent silent-write ObjectForm had been arming on edit forms), static-only (fixing ObjectForm's verbatim seeding of NOW()/current_user/CEL envelopes — a regression that would otherwise have shipped under this fix, given ~100 platform objects declare NOW()), and caller-values-win. defaultValue only is honored, not option-level default: true — measured against the server's applyFieldDefaults (one key is honored because one key is enforced; no UI-only second contract). Changeset patch + README/guide docs.

    Review notes: the card's mechanism hypothesis (and my dispatch assumption) was refuted with evidence — ObjectForm was never on the create dialog's path; ModalForm and four sibling containers each carried their own schema-blind create branch. The dev fixed at the true producer with one module instead of five copies — acknowledged openly here as the process working. Test-first: the red baseline (9 failed on origin/main) IS the reverse verification, plus a targeted gate-removal check that flips exactly one assertion (and the report explains why the status: null variant would have been green for an empty reason — that level of test skepticism is exactly right). Consumer sweep over plugin-view/plugin-designer/app-shell: 3056 green. 11 files all in packages/plugin-form + docs; GridField* and app-shell untouched. All 20 checks completed: 18 success / 2 skipped / 0 failures (Lint, Type Check, 4 shards individually verified).

    Out-of-scope findings verified filed: #4069 (required + runtime-token default is unsubmittable — one layer down, four dispositions listed; triaged next) and objectstack#7246 (SelectOption.default declared-but-unenforced, finding — the one consumer is a lint heuristic that draws a build-breaking conclusion from an unenforced declaration; ADR-0049 territory for the framework triage seat).

    Landing next: flipping ready and driving to MERGED.


    Generated by Claude Code

  4. added a commit that references this issue on Aug 10, 2026
    f0c9a90
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions