feat: allow easier client-side form validation (closes #413) (#620)

This commit is contained in:
Greg Johnston 2023-03-03 13:19:54 -05:00 committed by GitHub
parent 11d9018e4f
commit bd86125629
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
2 changed files with 56 additions and 15 deletions

View file

@ -129,7 +129,20 @@ pub fn Todos(cx: Scope) -> impl IntoView {
view! {
cx,
<div>
<MultiActionForm action=add_todo>
<MultiActionForm
// we can handle client-side validation in the on:submit event
// leptos_router implements a `FromFormData` trait that lets you
// parse deserializable types from form data and check them
on:submit=move |ev| {
let data = AddTodo::from_event(&ev).expect("to parse form data");
// silly example of validation: if the todo is "nope!", nope it
if data.title == "nope!" {
// ev.prevent_default() will prevent form submission
ev.prevent_default();
}
}
action=add_todo
>
<label>
"Add a Todo"
<input type="text" name="title"/>

View file

@ -210,7 +210,7 @@ where
let input = action.input();
let on_form_data = Rc::new(move |form_data: &web_sys::FormData| {
let data = action_input_from_form_data(form_data);
let data = I::from_form_data(form_data);
match data {
Ok(data) => {
input.set(Some(data));
@ -301,11 +301,7 @@ where
return;
}
let (form, _, _, _) = extract_form_attributes(&ev);
let form_data = web_sys::FormData::new_with_form(&form).unwrap_throw();
let data = action_input_from_form_data(&form_data);
match data {
match I::from_event(&ev) {
Err(e) => error!("{e}"),
Ok(input) => {
ev.prevent_default();
@ -433,12 +429,44 @@ fn extract_form_attributes(
}
}
fn action_input_from_form_data<I: serde::de::DeserializeOwned>(
form_data: &web_sys::FormData,
) -> Result<I, serde_urlencoded::de::Error> {
let data =
web_sys::UrlSearchParams::new_with_str_sequence_sequence(form_data)
.unwrap_throw();
let data = data.to_string().as_string().unwrap_or_default();
serde_urlencoded::from_str::<I>(&data)
/// Tries to deserialize a type from form data. This can be used for client-side
/// validation during form submission.
pub trait FromFormData
where
Self: Sized + serde::de::DeserializeOwned,
{
/// Tries to deserialize the data, given only the `submit` event.
fn from_event(
ev: &web_sys::Event,
) -> Result<Self, serde_urlencoded::de::Error>;
/// Tries to deserialize the data, given the actual form data.
fn from_form_data(
form_data: &web_sys::FormData,
) -> Result<Self, serde_urlencoded::de::Error>;
}
impl<T> FromFormData for T
where
T: serde::de::DeserializeOwned,
{
fn from_event(
ev: &web_sys::Event,
) -> Result<Self, serde_urlencoded::de::Error> {
let (form, method, action, enctype) = extract_form_attributes(&ev);
let form_data = web_sys::FormData::new_with_form(&form).unwrap_throw();
Self::from_form_data(&form_data)
}
fn from_form_data(
form_data: &web_sys::FormData,
) -> Result<Self, serde_urlencoded::de::Error> {
let data =
web_sys::UrlSearchParams::new_with_str_sequence_sequence(form_data)
.unwrap_throw();
let data = data.to_string().as_string().unwrap_or_default();
serde_urlencoded::from_str::<Self>(&data)
}
}