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

6.6 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. This slice validates Computed bindings edited in Source; it does not add a Computed-binding authoring 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 bindings-v3 capability and metadata format 3 add Controller/ViewModel identity and Computed signatures, plus the captured resource on requests. 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 and a Computed authoring form remain 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.