Merge pull request #1610 from bunnyBites/get-parsed-values-for-form-values

Serializing FormData while form 'onsubmit'.
This commit is contained in:
Jonathan Kelley authored and GitHub committed 2024-01-08 10:35:26 -08:00
commit 298f5c7b93
6 files changed
+85 -27

No files matched your search

-3
View File
@@ -51,10 +51,7 @@ serde_json = "1"
[features]
default = ["serialize", "mounted", "eval"]
serialize = [
"serde",
"serde/rc",
"serde_repr",
"serde_json",
"euclid/serde",
"keyboard-types/serde",
"dioxus-core/serialize",
+48 -10
View File
@@ -4,6 +4,20 @@ use dioxus_core::Event;
pub type FormEvent = Event<FormData>;
/// A form value that may either be a list of values or a single value
#[cfg_attr(
feature = "serialize",
derive(serde::Serialize, serde::Deserialize),
// this will serialize Text(String) -> String and VecText(Vec<String>) to Vec<String>
serde(untagged)
)]
#[derive(Debug, Clone, PartialEq)]
pub enum FormValue {
Text(String),
VecText(Vec<String>),
}
/* DOMEvent: Send + SyncTarget relatedTarget */
pub struct FormData {
inner: Box<dyn HasFormData>,
}
@@ -43,7 +57,7 @@ impl FormData {
}
/// Get the values of the form event
pub fn values(&self) -> HashMap<String, Vec<String>> {
pub fn values(&self) -> HashMap<String, FormValue> {
self.inner.values()
}
@@ -64,7 +78,7 @@ pub trait HasFormData: std::any::Any {
Default::default()
}
fn values(&self) -> HashMap<String, Vec<String>> {
fn values(&self) -> HashMap<String, FormValue> {
Default::default()
}
@@ -76,13 +90,37 @@ pub trait HasFormData: std::any::Any {
fn as_any(&self) -> &dyn std::any::Any;
}
impl FormData {
#[cfg(feature = "serialize")]
/// Parse the values into a struct with one field per value
pub fn parsed_values<T>(&self) -> Result<T, serde_json::Error>
where
T: serde::de::DeserializeOwned,
{
use serde::Serialize;
fn convert_hashmap_to_json<K, V>(hashmap: &HashMap<K, V>) -> serde_json::Result<String>
where
K: Serialize + std::hash::Hash + Eq,
V: Serialize,
{
serde_json::to_string(hashmap)
}
let parsed_json =
convert_hashmap_to_json(&self.values()).expect("Failed to parse values to JSON");
serde_json::from_str(&parsed_json)
}
}
#[cfg(feature = "serialize")]
/// A serialized form data object
#[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
pub struct SerializedFormData {
value: String,
values: HashMap<String, Vec<String>>,
files: Option<std::sync::Arc<SerializedFileEngine>>,
values: HashMap<String, FormValue>,
files: Option<SerializedFileEngine>,
}
#[cfg(feature = "serialize")]
@@ -90,8 +128,8 @@ impl SerializedFormData {
/// Create a new serialized form data object
pub fn new(
value: String,
values: HashMap<String, Vec<String>>,
files: Option<std::sync::Arc<SerializedFileEngine>>,
values: HashMap<String, FormValue>,
files: Option<SerializedFileEngine>,
) -> Self {
Self {
value,
@@ -114,9 +152,9 @@ impl SerializedFormData {
resolved_files.insert(file, bytes.unwrap_or_default());
}
Some(std::sync::Arc::new(SerializedFileEngine {
Some(SerializedFileEngine {
files: resolved_files,
}))
})
}
None => None,
},
@@ -138,14 +176,14 @@ impl HasFormData for SerializedFormData {
self.value.clone()
}
fn values(&self) -> HashMap<String, Vec<String>> {
fn values(&self) -> HashMap<String, FormValue> {
self.values.clone()
}
fn files(&self) -> Option<std::sync::Arc<dyn FileEngine>> {
self.files
.as_ref()
.map(|files| std::sync::Arc::clone(files) as std::sync::Arc<dyn FileEngine>)
.map(|files| std::sync::Arc::new(files.clone()) as _)
}
fn as_any(&self) -> &dyn std::any::Any {
+12 -2
View File
@@ -109,8 +109,18 @@ function handler(event, name, bubbles, config) {
const formData = new FormData(target);
for (let name of formData.keys()) {
let value = formData.getAll(name);
contents.values[name] = value;
const fieldType = target.elements[name].type;
switch (fieldType) {
case "select-multiple":
contents.values[name] = formData.getAll(name);
break;
// add cases for fieldTypes that can hold multiple values here
default:
contents.values[name] = formData.get(name);
break;
}
}
}
+3 -2
View File
@@ -18,6 +18,7 @@ use dioxus_html::input_data::keyboard_types::{Code, Key, Location, Modifiers};
use dioxus_html::input_data::{
MouseButton as DioxusMouseButton, MouseButtonSet as DioxusMouseButtons,
};
use dioxus_html::FormValue;
use dioxus_html::{event_bubbles, prelude::*};
use std::any::Any;
use std::collections::HashMap;
@@ -66,7 +67,7 @@ impl EventData {
pub struct FormData {
pub(crate) value: String,
pub(crate) values: HashMap<String, Vec<String>>,
pub values: HashMap<String, FormValue>,
pub(crate) files: Option<Files>,
}
@@ -76,7 +77,7 @@ impl HasFormData for FormData {
self.value.clone()
}
fn values(&self) -> HashMap<String, Vec<String>> {
fn values(&self) -> HashMap<String, FormValue> {
self.values.clone()
}
+3 -3
View File
@@ -9,9 +9,9 @@
use dioxus_core::{
BorrowedAttributeValue, ElementId, Mutation, Template, TemplateAttribute, TemplateNode,
};
use dioxus_html::{event_bubbles, MountedData, PlatformEventData};
use dioxus_interpreter_js::get_node;
use dioxus_interpreter_js::{minimal_bindings, save_template, Channel};
use dioxus_html::PlatformEventData;
use dioxus_html::{event_bubbles, MountedData};
use dioxus_interpreter_js::{get_node, minimal_bindings, save_template, Channel};
use futures_channel::mpsc;
use rustc_hash::FxHashMap;
use wasm_bindgen::{closure::Closure, JsCast, JsValue};
+19 -7
View File
@@ -1,11 +1,11 @@
use std::{any::Any, collections::HashMap};
use dioxus_html::{
FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter, ImageData, MountedData,
PlatformEventData, ScrollData,
prelude::FormValue, FileEngine, FormData, HasFormData, HasImageData, HtmlEventConverter,
ImageData, MountedData, PlatformEventData, ScrollData,
};
use js_sys::Array;
use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
use wasm_bindgen::{prelude::wasm_bindgen, JsCast, JsValue};
use web_sys::{Document, Element, Event};
pub(crate) struct WebEventConverter;
@@ -378,7 +378,7 @@ impl HasFormData for WebFormData {
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
}
fn values(&self) -> HashMap<String, Vec<String>> {
fn values(&self) -> HashMap<String, FormValue> {
let mut values = std::collections::HashMap::new();
// try to fill in form values
@@ -388,10 +388,12 @@ impl HasFormData for WebFormData {
if let Ok(array) = value.dyn_into::<Array>() {
if let Some(name) = array.get(0).as_string() {
if let Ok(item_values) = array.get(1).dyn_into::<Array>() {
let item_values =
let item_values: Vec<String> =
item_values.iter().filter_map(|v| v.as_string()).collect();
values.insert(name, item_values);
values.insert(name, FormValue::VecText(item_values));
} else if let Ok(item_value) = array.get(1).dyn_into::<JsValue>() {
values.insert(name, FormValue::Text(item_value.as_string().unwrap()));
}
}
}
@@ -432,7 +434,17 @@ impl HasFormData for WebFormData {
const formData = new FormData(form);
for (let name of formData.keys()) {
values.set(name, formData.getAll(name));
const fieldType = target.elements[name].type;
switch (fieldType) {
case "select-multiple":
contents.values[name] = formData.getAll(name);
break;
// add cases for fieldTypes that can hold multiple values here
default:
contents.values[name] = formData.get(name);
break;
}
return values;