{{ slice.errors.value.tier }}
+ +``` + +The composable resolves the key to the `addon:example-addon:` namespace on the page's form, finds that form above it in the component tree, and returns a view scoped to the namespace. It throws a descriptive error when called outside a page that hosts slices, such as a listing page. + +`useFormSlice` returns: + +| Property | Purpose | +|:---------|:--------| +| `values` | A reactive object holding only this slice's fields, by bare name. `v-model="slice.values.tier"` reads and writes `addon:example-addon:tier` on the page's form. Writing to a field the slice did not declare throws. | +| `errors` | Validation errors for this namespace, keyed by bare field. | +| `field(name)` | A writable ref for one field, for components that prefer a ref to a property path. | +| `isDirty` | Whether any of the namespace's fields differ from their pristine value. Changes elsewhere on the page leave it `false`. | +| `saving` / `committing` | The page form's status refs, passed through for a local indicator. | + +The composable never exposes the underlying form, so a component has no path to another namespace's keys or to the record's own fields. The server enforces the same boundary independently: a request carrying `example-addon:tier` without the `addon:` prefix is rejected as an unknown field on a drafted page and ignored by the slice composer on a plain one. + +## Drafted edit pages + +On the first-party edit pages (customers, products, brands, collections, product types, variants) the form is an [edit draft](/2.x/admin/extending/edit-drafts). Every registered slice is seeded up front from the shared `formSliceValues` prop, and its fields behave exactly like the record's own: + +- **Autosave and restore.** Values persist as staff type and come back when they return to the record, with the same restored-draft banner and **Discard** action. +- **Dirty guard.** Leaving the page with an unsaved tier prompts first. +- **Conflict detection.** If another staff member changes the tier before this draft commits, the page's conflict dialog lists it under the label from `labels()`, with keep-mine and take-theirs choices, and nothing commits until it is resolved. +- **Validation.** A failed rule maps to `errors.tier` on the bound component, and the whole commit is refused. +- **Atomic commit.** Every slice's `commit()` runs after the record's own inside one transaction. If one throws, the record's changes roll back. + +## Plain create and settings forms + +The create pages and the settings edit forms post a plain Inertia form and redirect. They compose the same slices, with three differences that follow from a plain form carrying only what it posts: + +- **Binding claims the namespace.** A slice's keys join the form only when a component binds to it with `useFormSlice`. A page nobody extends posts exactly what it did before. +- **Only submitted namespaces commit.** The slice's `commit()` runs when the request holds at least one of its keys, with the slice's current values overlaid by the submitted ones. Absent namespaces are untouched. +- **Slice rules apply when present.** Each rule is composed under `sometimes`, so an unbound namespace cannot fail validation. Within a bound namespace the slice's rules apply in full, `required` included, and errors surface in the same round as the form's own. + +On a create page, `fields()`, `currentValues()`, and `rules()` see a fresh, unsaved instance of the model, and `commit()` receives the record the store action created, inside its transaction. Nothing autosaves or conflict-checks, and `discard()` never runs. + +The settings index pages' inline create dialogs expose no slot zone, so there is nothing to bind there; the roles form, the auth and account forms, and the order view are not composed. + +