diff --git a/docs/skills/form-input/SKILL.md b/docs/skills/form-input/SKILL.md index 8c2d6b028..e5d5578eb 100755 --- a/docs/skills/form-input/SKILL.md +++ b/docs/skills/form-input/SKILL.md @@ -9,6 +9,21 @@ description: Creating custom form input components that extend Form_Input_Abstra Form input components extend `Form_Input_Abstract` which implements a template method pattern. The base class handles value buffering, events, and the val() interface. Concrete classes only implement how to get and set values. +## Core Contract: Timing Indifference + +**Callers must never care about component lifecycle timing.** + +`.val(value)` must produce identical results whether called: +- Before the component initializes (buffered, applied at `_mark_ready()`) +- After the component initializes (applied immediately via `_set_value()`) +- At any point during the component's lifecycle + +This is non-negotiable. If a caller needs to use `await component.ready()` or timing tricks to make `val()` work, **the component is broken**. + +**Implementation requirement**: Your `_set_value()` must work identically whether called during initial value application (via `_mark_ready()`) or later (via direct `val()` calls). If your implementation relies on initialization state or timing, you've violated this contract. + +--- + ## Template Method Pattern **Base class handles:** @@ -76,6 +91,18 @@ _set_value(value) { } ``` +### String/Number Value Coercion + +Input components must accept both string and numeric values. A select with options `"3"`, `"5"`, `"9"` must work with `.val(3)` or `.val("3")`. + +```javascript +_set_value(value) { + // Convert to string for comparison with option values + const str_value = value == null ? '' : str(value); + this.tom_select.setValue(str_value, true); +} +``` + ### _mark_ready() Call in `on_ready()` to apply buffered values and mark component ready: