Svelte / Basic: reactive components and ownership / Lesson 5
Bindings, forms and accessible input contracts
Use convenient browser bindings while preserving explicit value types, validation and server authority.
On this page
Lesson goals
- Explain bind:value and why browser constraints do not replace runtime validation.
- Design labeled inputs and meaningful pending/error feedback.
Two-way browser binding is a UI mechanism
bind:value connects supported input values to component state, making a control easier to read and update. It does not turn every browser value into a valid domain quantity. Numeric inputs have documented coercion and empty/invalid-value behavior, so a number-like field can still need handling for absence. min, max and required improve browser interaction but are not server authorization or an unbypassable schema. Keep the input’s displayed text, parsed value and accepted business value distinct. A local change should not silently become a durable write merely because the field is bound to reactive state.
Label the control and guard a derived preview
The fragment binds a numeric seat count and derives whether it is a permitted preview value. The wrapping label avoids relying on an unowned global element ID. It displays a status rather than submitting data. The numeric binding’s actual DOM coercion and update order were not executed. A real form should associate field problems and submit outcomes accessibly and preserve useful entered text on expected rejection. Disabling a submit button is helpful interaction but cannot guarantee one server effect or stop a forged request. Server-side parsing and current object policy remain mandatory.
<script>
let seats = $state(1);
let valid = $derived(Number.isInteger(seats) && seats >= 1 && seats <= 20);
</script>
<label>Seats <input type="number" min="1" max="20" bind:value={seats} /></label>
<p>{valid ? "Valid preview" : "Enter a whole number from 1 to 20"}</p>
The range is a synthetic preview contract. No browser input, binding coercion or form submission was run.
Parse submitted values at the server boundary
HTML form submission can produce repeated names and FormData values that are text, file-like data or null. Do not stringify every value and assume the result is a valid title or count. Define unknown-field, cardinality and patch-presence rules: omission, explicit clearing and an empty string can carry different meanings. Choose a canonical server representation and return safe field problems rather than raw source exceptions. A Svelte binding and a TypeScript annotation do not execute this runtime validation. SvelteKit actions later provide a server entry point, but the application must still enforce the domain contract there.
Reject partial numeric parsing locally
The local parser requires one complete positive decimal string and limits seats to twenty. It rejects an array-like object, trailing text and out-of-range input before a source effect exists. This is an explicit lab convention rather than a universal number parser. It does not model browser numeric binding, locale-specific typing or native validity UI. A real server should apply the same rule through its FormData adapter and test missing and repeated fields separately. Do not let parseInt accept a numeric prefix and turn an unrelated string into a valid business count.
function seatCount(value) {
if(typeof value!=="string" || !/^[1-9][0-9]*$/.test(value))throw new Error("INVALID_SEATS");
const n=Number(value);if(!Number.isInteger(n)||n>20)throw new Error("INVALID_SEATS");return n;
}
console.assert(seatCount("12")===12);
for(const value of ["12seats","21",["2","3"]]){let rejected=false;try{seatCount(value);}catch{rejected=true;}console.assert(rejected);}
The valid twelve is accepted; partial text, excessive count and array input reject. No DOM or server action is tested.
Verify the whole editing task
Use semantic controls, meaningful labels, keyboard operation and field-associated feedback. Pending UI should communicate what is happening without erasing useful input or hiding the eventual outcome. Native browser validation and enhancement can differ when JavaScript is unavailable, so test the selected progressive flow deliberately. Keep privacy in error messages and do not echo sensitive submitted values into a public response. This course executes only seatCount; numeric DOM bindings, form reset behavior, screen-reader output and actual server rejection remain unexecuted. A real authorized browser/server test must verify those boundaries with safe synthetic records and exact framework versions.
Common errors and fixes
A browser min/max is treated as complete validation
- Why it happens
- UI constraints were mistaken for a nonbypassable server contract.
- How to fix it
- Validate submitted runtime values and current authority on the server.
- Prevent it next time
- Test direct malformed requests independently of the UI.
12seats becomes an accepted count of twelve
- Why it happens
- Prefix parsing replaced whole-value validation.
- How to fix it
- Check the complete syntax and range before conversion.
- Prevent it next time
- Retain trailing-text and boundary fixtures.
A rejected form loses all useful input
- Why it happens
- Expected field problems were handled as an opaque reset.
- How to fix it
- Return safe structured feedback and preserve appropriate draft values.
- Prevent it next time
- Test keyboard recovery and rejection with enhancement on and off.
Check your understanding
1. Does a valid bound seat count authorize reserving those seats?
Show worked solution
No. It describes a local UI value. The server must still verify type, availability, current principal and the permitted operation, then use a coherent write protocol. A caller can bypass bindings and browser constraints entirely. Test denied and conflicting requests as well as a valid preview.
2. Why does seatCount not test numeric bind:value behavior?
Show worked solution
It receives an explicit JavaScript value and applies a domain parser. It never creates an input element, triggers browser events or runs Svelte’s binding code. Keep the pure rule test and separately verify empty, invalid, reset and locale interaction behavior in the actual compiled control when authorized.
Key takeaways
- Bindings improve interaction without replacing server validation.
- Accessible feedback and canonical input meaning belong in the contract.