#[derive(FormInput)]
{
// Attributes available to this derive:
#[form]
}
Expand description
Derives FormInput for a typed value a form posts under its own
keys: what an Action asks for before it runs, what a sign-up reads, or
one item of a repeater.
#[derive(tablo_core::FormInput)]
pub struct Rejection {
#[form(multiline = 4)]
pub reason: String,
pub notify_author: bool,
}
struct Reject;
impl Action<PostResource> for Reject {
type Input = Rejection;
const NAME: &'static str = "reject";
async fn run(
_cx: &Cx,
posts: &[Post],
rejection: Rejection,
ex: &mut dyn toasty::Executor,
) -> Result<tablo_core::Outcome, tablo_core::WriteError> {
// Store `rejection.reason`, notify when `rejection.notify_author` holds…
Ok(tablo_core::Outcome::done())
}
}Each field posts its own name, which no column binds, and renders the control its type
picks: a bool a checkbox, #[form(options)] a choice over the field type’s
Options, #[form(options = T)] one over T’s, and any other
FormScalar a text input of the type’s input type.
One item of a repeater is an embedded struct a #[document] list stores, which a
RecordForm edits with #[form(repeat)]:
#[derive(Debug, Clone, toasty::Embed, tablo_core::FormInput)]
pub struct Link {
pub label: String,
#[form(label = "URL")]
pub url: String,
}
#[derive(Debug, Clone, toasty::Model, tablo_core::ModelColumns)]
pub struct Post {
#[key]
#[auto]
id: uuid::Uuid,
#[document]
#[default(Vec::<Link>::new())]
links: Vec<Link>,
}
#[derive(tablo_core::RecordForm)]
#[form(model = Post)]
pub struct PostForm {
#[form(repeat)]
pub links: Vec<Link>,
}Each row posts the item’s keys under its own prefix (links.0.url) and answers to its
controls’ own rules, such as a choice offering only its options. A repeater takes at most
MAX_REPEATER_ROWS rows.
A field’s blank answer is its #[form(blank = ..)], "" for an #[form(optional)]
String, None for an Option, or false for a bool. A field with none is required:
its control renders required and the parse refuses an empty submission.
§Per-field attributes
#[form(label = "Reason")]— the control’s label (default: the field name in sentence case).#[form(multiline = 4)]— a<textarea>of 4 rows.#[form(placeholder = "rust, async")]— a text input’s placeholder.#[form(email)]— a text input that refuses a malformed email address.#[form(url)]— a text input that refuses what is not anhttporhttpsURL:TextField::url.#[form(tel)]— a telephone text input, in no checked format:TextField::tel.#[form(password)]— a masked text input on aString, read untrimmed, that a refused form renders empty. A repeater refuses one in its item at mount: an edit could not render the stored secret back, so each save would overwrite it.#[form(blank = ..)],#[form(optional)]— the blank answer.#[form(options)],#[form(options = T)]— a choice. AnOption<T>field names its options type:#[form(options = T)].
A generic struct, a tuple struct, an empty struct (name () instead), an unknown key, a
text input’s key with options or on a bool, password with multiline, two of
email, url, tel and password, and a field
that is not a FormScalar are compile errors.
RouterBuilderPanelExt::panel refuses an action’s
input with a field named csrf_token, confirm or ids, and a sign-up’s with one named
csrf_token or next: the keys each POST carries besides its input.
A hand-written impl builds its fields with Field::text_input,
Field::choice_input
and Field::toggle_input; its controls render without the
required mark, though its parse still refuses what it requires. Its write fills a
repeater row from a stored item, and writes nothing for a type no repeater holds.
Derive FormInput for a typed value a form posts under its own keys; documented on the
tablo-core re-export.