diff --git a/packages/html/Cargo.toml b/packages/html/Cargo.toml
index 8d86a9672..d3efc839b 100644
--- a/packages/html/Cargo.toml
+++ b/packages/html/Cargo.toml
@@ -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",
diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs
index de8bb0dbf..ca1686cb7 100644
--- a/packages/html/src/events/form.rs
+++ b/packages/html/src/events/form.rs
@@ -4,6 +4,20 @@ use dioxus_core::Event;
pub type FormEvent = Event;
+/// 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) to Vec
+ serde(untagged)
+)]
+#[derive(Debug, Clone, PartialEq)]
+pub enum FormValue {
+ Text(String),
+ VecText(Vec),
+}
+
+/* DOMEvent: Send + SyncTarget relatedTarget */
pub struct FormData {
inner: Box,
}
@@ -43,7 +57,7 @@ impl FormData {
}
/// Get the values of the form event
- pub fn values(&self) -> HashMap> {
+ pub fn values(&self) -> HashMap {
self.inner.values()
}
@@ -64,7 +78,7 @@ pub trait HasFormData: std::any::Any {
Default::default()
}
- fn values(&self) -> HashMap> {
+ fn values(&self) -> HashMap {
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(&self) -> Result
+ where
+ T: serde::de::DeserializeOwned,
+ {
+ use serde::Serialize;
+
+ fn convert_hashmap_to_json(hashmap: &HashMap) -> serde_json::Result
+ 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>,
- files: Option>,
+ values: HashMap,
+ files: Option,
}
#[cfg(feature = "serialize")]
@@ -90,8 +128,8 @@ impl SerializedFormData {
/// Create a new serialized form data object
pub fn new(
value: String,
- values: HashMap>,
- files: Option>,
+ values: HashMap,
+ files: Option,
) -> 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> {
+ fn values(&self) -> HashMap {
self.values.clone()
}
fn files(&self) -> Option> {
self.files
.as_ref()
- .map(|files| std::sync::Arc::clone(files) as std::sync::Arc)
+ .map(|files| std::sync::Arc::new(files.clone()) as _)
}
fn as_any(&self) -> &dyn std::any::Any {
diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js
index 54f1d517c..10e07a385 100644
--- a/packages/interpreter/src/interpreter.js
+++ b/packages/interpreter/src/interpreter.js
@@ -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;
+ }
}
}
diff --git a/packages/rink/src/hooks.rs b/packages/rink/src/hooks.rs
index 1ffa361f7..012c0c1eb 100644
--- a/packages/rink/src/hooks.rs
+++ b/packages/rink/src/hooks.rs
@@ -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>,
+ pub values: HashMap,
pub(crate) files: Option,
}
@@ -76,7 +77,7 @@ impl HasFormData for FormData {
self.value.clone()
}
- fn values(&self) -> HashMap> {
+ fn values(&self) -> HashMap {
self.values.clone()
}
diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs
index da46e5012..9d08f09f4 100644
--- a/packages/web/src/dom.rs
+++ b/packages/web/src/dom.rs
@@ -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};
diff --git a/packages/web/src/event.rs b/packages/web/src/event.rs
index d1eed67a6..994bf304a 100644
--- a/packages/web/src/event.rs
+++ b/packages/web/src/event.rs
@@ -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> {
+ fn values(&self) -> HashMap {
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::() {
if let Some(name) = array.get(0).as_string() {
if let Ok(item_values) = array.get(1).dyn_into::() {
- let item_values =
+ let item_values: Vec =
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::() {
+ 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;