# `MishkaCmsCore.Builder.FormRole`
[🔗](https://github.com/mishka-group/mishka_cms_core/blob/v0.0.1/lib/mishka_cms_core/builder/form_role.ex#L5)

What part of a form a component plays, declared by whoever wrote it rather than guessed here.

    "extra": {"form": {"role": "field", "field": "bind", "name": "input_name", "label": "caption"}}

`role` is the only required key and is one of `wrapper`, `field`, `error`, `fieldset`, `submit`.
Every other key NAMES ONE OF THIS COMPONENT'S OWN ATTRIBUTES, so a kit that calls its
`Phoenix.HTML.FormField` attribute `bind` says so, and the editor writes `bind={f[:email]}` without
ever knowing the word "field".

`field`, `name` and `value` are attributes the editor WRITES; `label` is one it reads the author's
own word out of (`MishkaCmsCore.Runtime.PageBuilder.Fields.field_label/2`). Every one of the four is
an attribute, which is why a slot is not a candidate — see below.

## A wrong role is worse than no role

Same doctrine and same failure mode as `MishkaCmsCore.Builder.Furnishing`: this is a third party's
JSON, so anything unreadable answers `:none` and `MishkaCmsCore.Runtime.PageBuilder.Fields` infers
instead — which is what every kit shipped before this key existed gets, and is right most of the
time. A malformed declaration never costs the component, and a malformed component never costs the
kit (`MishkaCmsCore.UiKit.Contract`).

## A key that points at nothing is dropped

`build/2` keeps only the keys whose value names an ATTRIBUTE the component actually declares.
Writing `bind={f[:x]}` onto a component with no `bind` attribute is the one failure this module
exists to prevent: the attribute is dropped on the way to the renderer, the field is called with
neither a binding nor a name, and `KeyError :name` lands inside LiveView's diff.

A slot is not a candidate, and validating against attrs AND slots reopened the same failure from
the other side: `"field": "inner_block"` named something the component really does declare, so the
key survived, and the editor then wrote `inner_block={f[:email]}` — which is not an attribute the
component accepts either. Every key here names something the editor writes into or reads out of an
attribute, so an attribute is the only thing it can be.

# `t`

```elixir
@type t() :: %MishkaCmsCore.Builder.FormRole{bindings: map() | nil, role: String.t()}
```

# `build`

```elixir
@spec build(term(), map()) :: {:ok, t()} | :none
```

This component's declared role, or `:none`.

`:none` is not a failure — most components declare nothing, and inference is the answer for them.
It is also what a malformed declaration gets, for the reason in the moduledoc.

# `builder`

# `enforce_keys`

# `enforce_keys`

# `example`

A sample %MishkaCmsCore.Builder.FormRole{} populated from defaults + type-based fallbacks.

# `keys`

# `keys`

# `roles`

```elixir
@spec roles() :: [String.t()]
```

Every role a kit may declare, so it can be read rather than guessed.

---

*Consult [api-reference.md](api-reference.md) for complete listing*
