Merge pull request #1610 from bunnyBites/get-parsed-values-for-form-values
Serializing FormData while form 'onsubmit'.
This commit is contained in:
6 files changed
+85
-27
No files matched your search
@@ -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",
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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()
|
||||
}
|
||||
|
||||
|
||||
@@ -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};
|
||||
|
||||
@@ -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;
|
||||
|
||||
Reference in new issue
Block a user