Files
NeitzelFXDesigner/docs/viewmodel-computed-validation.md

6.7 KiB

Custom ViewModels and Computed bindings

The shared Bindings → Discover targets panel now identifies the declared Model, selected ViewModel and Controller, lists custom properties, and shows public zero-argument application methods as Computed sources. Find Converters / Validate and the automatic preview gate validate existing Direct and Computed declarations in both hosts. The subsequent Computed-binding inspector also uses this validation for declarations authored in the shared form.

Runtime selection and property precedence

Discovery reads NeitzelLib's @ViewModelClass on the FXML Controller. For Component fx:root documents it uses the captured resource path (package/ClassName.fxml) to identify the Component class, which is also the Controller. The worker receives this identity from the same unambiguous source-root mapping used for draft overlays. The root's actual Component class supplies its target properties, including additions beyond its FXML base type. An unavailable Component class/context is reported as unresolved; a conflicting fx:controller on Component FXML is an error.

With no explicit selection, discovery uses generic ViewModel capabilities, independent of incidental runtime reuse from another View. A subclass adds public no-argument accessors returning JavaFX Property to the inherited Model Attributes. Runtime precedence is preserved: reserved latestMessage wins, then Model Attributes, then additional property accessors. Generic nested Model paths also expose their message property. A replacement exposes only its own named JavaFX property API; it does not inherit Model Attributes, built-in message properties or dotted traversal through plain objects.

The selected class must be concrete and have a constructor accepting the declared Model. The most-specific compatible constructor is checked for access from NeitzelLib's module; incomparable overloads produce an ambiguity warning. No constructor or accessor runs during discovery. An overridden ViewModel.property(String) is dynamic: signature-derived results remain unverified and produce warnings. Raw/generic property signatures that cannot establish a value type stay unresolved. Read-only ObservableValue accessors are not advertised as bindable ViewModel properties, matching the runtime's requirement for Property.

Computed validation

Discovery excludes Object methods and inherited JDK/JavaFX framework methods from the application-method inventory. Void and parameterized methods are unavailable as sources. Public inherited application methods are included. Method bodies never run during discovery.

Validation checks the declared Controller, public zero-argument method, reflective accessibility to NeitzelLib, target fx:id, writable JavaFX property, duplicate Direct/Computed targets and boxed return-type assignability. Private @FXML methods are not sufficient for NeitzelLib Computed bindings. Object or unresolved value types yield warnings; missing methods, void results and known incompatible types are errors. Existing unprefixed method names remain accepted because the runtime also accepts them; the inventory displays #method.

Computed validation still runs when a Model declaration is missing: that limits Model discovery but does not hide a known Controller-method error. Errors block new previews while saving invalid drafts and normal host Undo/Redo remain available. Results retain build/draft freshness checks. Pending target discovery can also satisfy an automatic preview-validation request, avoiding replacement of the user's discovery operation by a canvas refresh.

Boundaries

Application classes stay in the target worker and are loaded without initialization. The current bindings-v5 capability and metadata format 5 retain Controller/ViewModel identity and Computed signatures, plus the captured resource on requests, and add Repeat options. Old metadata capabilities are rejected rather than silently decoded. Existing protocol identities, payload limits, path/target limits and worker cancellation apply; the method inventory is bounded to 256 entries.

Generated metadata, custom BeanInfo execution, arbitrary dynamic property resolvers, full generic inference, cross-View first-use conflicts, nested ViewModel selection caused by other Views/Components, collection authoring and dependency-injection viability remain outside this slice. Method bodies, null results, constructor side effects and Computed refresh behavior are runtime concerns; compatible signatures do not prove that application code will succeed. In particular, discovery does not change which additional properties NeitzelLib observes for Computed refresh. Complete IDE Problems integration remains later work.

Automated qualification

Unit checks cover subclass/replacement selection, inherited properties and methods, name precedence, built-in messages, constructor mismatch, dynamic resolvers, Component resource identity/root properties, method accessibility and return types, missing Model declarations and non-initialization.

Real classpath and named-module fixtures check custom, replacement and Component metadata without initializing application classes. Separate fresh target processes load subclass/replacement Views through NeitzelLib, exercise custom-property write-back, confirm Model-name precedence and execute Computed methods. Shared host contracts verify resource identity through the worker transport. Live standalone and registered IntelliJ checks inspect custom properties and methods, save a type-invalid Computed binding, verify preview blocking and exact Undo/Redo, and discover replacement properties without mutating FXML. Existing rendering, Converter, input and lifecycle checks remain enabled. No manual test is required; previously skipped human checks remain skipped.

Qualification completed on 2026-11-13 on the pinned macOS baseline: check-bootstrap.py passed with 91 unit/adapter tests (84 Maven, 7 IntelliJ), 36 fresh target processes / 484 fixture assertions, both native standalone launch modes, both connected host adapters and 48 worker lifecycle cycles. check-ide-host.py passed the registered IntelliJ editor scenarios for both classpath and module-path applications, including exclusive JavaFX 26 native-library verification and worker shutdown. No manual testing was used.

Local run evidence: /tmp/viewmodel-bootstrap.log, /tmp/viewmodel-registered-ide.log, target/connected-workflow/ and target/ide-workflow/{classpath,modulepath}.json. Malformed-XML diagnostics are intentional recovery-test inputs. No uncaught application exception or native crash was found in these qualification runs. This is evidence for the pinned baseline, not a broader platform certification.