dioxus_html/events/
form.rs1use crate::file_data::HasFileData;
2use crate::FileData;
3use std::fmt::Debug;
4
5use dioxus_core::Event;
6
7pub type FormEvent = Event<FormData>;
8
9pub struct FormData {
11 inner: Box<dyn HasFormData>,
12}
13
14impl FormData {
15 pub fn new(event: impl HasFormData + 'static) -> Self {
17 Self {
18 inner: Box::new(event),
19 }
20 }
21
22 pub fn value(&self) -> String {
24 self.inner.value()
25 }
26
27 pub fn parsed<T>(&self) -> Result<T, T::Err>
29 where
30 T: std::str::FromStr,
31 {
32 self.value().parse()
33 }
34
35 pub fn checked(&self) -> bool {
40 self.value().parse().unwrap_or(false)
41 }
42
43 pub fn values(&self) -> Vec<(String, FormValue)> {
47 self.inner.values()
48 }
49
50 pub fn get_first(&self, name: &str) -> Option<FormValue> {
52 self.values()
53 .into_iter()
54 .find_map(|(k, v)| if k == name { Some(v) } else { None })
55 }
56
57 pub fn get(&self, name: &str) -> Vec<FormValue> {
59 self.values()
60 .into_iter()
61 .filter_map(|(k, v)| if k == name { Some(v) } else { None })
62 .collect()
63 }
64
65 pub fn files(&self) -> Vec<FileData> {
67 self.inner.files()
68 }
69
70 #[inline(always)]
72 pub fn downcast<T: 'static>(&self) -> Option<&T> {
73 self.inner.as_any().downcast_ref::<T>()
74 }
75
76 pub fn valid(&self) -> bool {
78 !self.inner.value().is_empty()
79 }
80}
81
82impl FormData {
83 #[cfg(feature = "serialize")]
85 pub fn parsed_values<T>(&self) -> Result<T, serde_json::Error>
86 where
87 T: serde::de::DeserializeOwned,
88 {
89 use crate::SerializedFileData;
90
91 let values = &self.values();
92
93 let mut map = serde_json::Map::new();
94 for (key, value) in values {
95 let entry = map
96 .entry(key.clone())
97 .or_insert_with(|| serde_json::Value::Array(Vec::new()));
98
99 match value {
100 FormValue::Text(text) => {
101 entry
102 .as_array_mut()
103 .expect("entry should be an array")
104 .push(serde_json::Value::String(text.clone()));
105 }
106 FormValue::File(Some(file_data)) => {
109 let serialized = SerializedFileData {
110 path: file_data.path().to_owned(),
111 size: file_data.size(),
112 last_modified: file_data.last_modified(),
113 content_type: file_data.content_type(),
114 contents: None,
115 };
116 entry
117 .as_array_mut()
118 .expect("entry should be an array")
119 .push(serde_json::to_value(&serialized).unwrap_or(serde_json::Value::Null));
120 }
121 FormValue::File(None) => {
122 entry
123 .as_array_mut()
124 .expect("entry should be an array")
125 .push(
126 serde_json::to_value(SerializedFileData::empty())
127 .unwrap_or(serde_json::Value::Null),
128 );
129 }
130 }
131 }
132
133 let map = map
135 .into_iter()
136 .map(|(k, v)| match v {
137 serde_json::Value::Array(arr) if arr.len() == 1 => {
138 (k, arr.into_iter().next().unwrap())
139 }
140 _ => (k, v),
141 })
142 .collect::<serde_json::Map<String, serde_json::Value>>();
143
144 serde_json::from_value(serde_json::Value::Object(map))
145 }
146}
147
148impl HasFileData for FormData {
149 fn files(&self) -> Vec<FileData> {
150 self.inner.files()
151 }
152}
153
154impl<E: HasFormData> From<E> for FormData {
155 fn from(e: E) -> Self {
156 Self { inner: Box::new(e) }
157 }
158}
159
160impl PartialEq for FormData {
161 fn eq(&self, other: &Self) -> bool {
162 self.value() == other.value() && self.values() == other.values()
163 }
164}
165
166impl Debug for FormData {
167 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
168 f.debug_struct("FormEvent")
169 .field("value", &self.value())
170 .field("values", &self.values())
171 .field("valid", &self.valid())
172 .finish()
173 }
174}
175
176#[derive(Debug, Clone, PartialEq)]
178pub enum FormValue {
179 Text(String),
180 File(Option<FileData>),
181}
182
183impl PartialEq<str> for FormValue {
184 fn eq(&self, other: &str) -> bool {
185 match self {
186 FormValue::Text(s) => s == other,
187 FormValue::File(_f) => false,
188 }
189 }
190}
191
192impl PartialEq<&str> for FormValue {
193 fn eq(&self, other: &&str) -> bool {
194 match self {
195 FormValue::Text(s) => s == other,
196 FormValue::File(_f) => false,
197 }
198 }
199}
200
201pub trait HasFormData: HasFileData + std::any::Any {
203 fn value(&self) -> String;
204
205 fn valid(&self) -> bool;
206
207 fn values(&self) -> Vec<(String, FormValue)>;
208
209 fn as_any(&self) -> &dyn std::any::Any;
211}
212
213#[cfg(feature = "serialize")]
214pub use serialize::*;
215
216#[cfg(feature = "serialize")]
217mod serialize {
218 use crate::SerializedFileData;
219
220 use super::*;
221
222 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
224 pub struct SerializedFormData {
225 #[serde(default)]
226 pub value: String,
227
228 #[serde(default)]
229 pub values: Vec<SerializedFormObject>,
230
231 #[serde(default)]
232 pub valid: bool,
233 }
234
235 #[derive(serde::Serialize, serde::Deserialize, Debug, PartialEq, Clone)]
236 pub struct SerializedFormObject {
237 pub key: String,
238 pub text: Option<String>,
239 pub file: Option<SerializedFileData>,
240 }
241
242 #[cfg(feature = "serialize")]
243 impl SerializedFormData {
244 pub fn new(value: String, values: Vec<SerializedFormObject>) -> Self {
246 Self {
247 value,
248 values,
249 valid: true,
250 }
251 }
252
253 fn from_form_lossy(data: &FormData) -> Self {
255 if let Some(data) = data.downcast::<SerializedFormData>() {
256 return data.clone();
257 }
258
259 let values = data
260 .values()
261 .iter()
262 .map(|(key, value)| match value {
263 FormValue::Text(s) => SerializedFormObject {
264 key: key.clone(),
265 text: Some(s.to_string()),
266 file: None,
267 },
268 FormValue::File(f) => SerializedFormObject {
269 key: key.clone(),
270 text: None,
271 file: if let Some(f) = f {
272 Some(SerializedFileData {
273 path: f.path(),
274 size: f.size(),
275 last_modified: f.last_modified(),
276 content_type: f.content_type(),
277 contents: None,
278 })
279 } else {
280 Some(SerializedFileData::empty())
281 },
282 },
283 })
284 .collect();
285
286 Self {
287 values,
288 value: data.value(),
289 valid: data.valid(),
290 }
291 }
292 }
293
294 impl HasFormData for SerializedFormData {
295 fn value(&self) -> String {
296 self.value.clone()
297 }
298
299 fn values(&self) -> Vec<(String, FormValue)> {
300 self.values
301 .iter()
302 .map(|v| {
303 let value = if let Some(text) = &v.text {
304 FormValue::Text(text.clone())
305 } else if let Some(_file) = &v.file {
306 FormValue::File(None)
308 } else {
309 FormValue::File(None)
310 };
311 (v.key.clone(), value)
312 })
313 .collect()
314 }
315
316 fn valid(&self) -> bool {
317 self.valid
318 }
319
320 fn as_any(&self) -> &dyn std::any::Any {
321 self
322 }
323 }
324
325 impl HasFileData for SerializedFormData {
326 fn files(&self) -> Vec<FileData> {
327 self.values
328 .iter()
329 .filter_map(|v| v.file.as_ref().map(|f| FileData::new(f.clone())))
330 .collect()
331 }
332 }
333
334 impl serde::Serialize for FormData {
335 fn serialize<S: serde::Serializer>(&self, serializer: S) -> Result<S::Ok, S::Error> {
336 SerializedFormData::from_form_lossy(self).serialize(serializer)
337 }
338 }
339
340 impl<'de> serde::Deserialize<'de> for FormData {
341 fn deserialize<D: serde::Deserializer<'de>>(deserializer: D) -> Result<Self, D::Error> {
342 let data = SerializedFormData::deserialize(deserializer)?;
343 Ok(Self {
344 inner: Box::new(data),
345 })
346 }
347 }
348}
349
350impl_event! {
351 FormData;
352
353 onchange
355
356 oninput
401
402 oninvalid
404
405 onreset
407
408 onsubmit
410}