dioxus_web/events/
form.rs1use super::WebEventExt;
2use crate::WebFileData;
3use dioxus_html::{FileData, FormValue, HasFileData, HasFormData};
4use js_sys::Array;
5use std::any::Any;
6use wasm_bindgen::{prelude::wasm_bindgen, JsCast};
7use web_sys::{Element, Event, FileReader};
8
9pub(crate) struct WebFormData {
10 element: Element,
11 event: Event,
12}
13
14impl WebEventExt for dioxus_html::FormData {
15 type WebEvent = Event;
16
17 #[inline(always)]
18 fn try_as_web_event(&self) -> Option<Self::WebEvent> {
19 self.downcast::<Event>().cloned()
20 }
21}
22
23impl WebFormData {
24 pub fn new(element: Element, event: Event) -> Self {
25 Self { element, event }
26 }
27}
28
29impl HasFormData for WebFormData {
30 fn value(&self) -> String {
31 let target = &self.element;
32 target
33 .dyn_ref()
34 .map(|input: &web_sys::HtmlInputElement| {
35 match input.type_().as_str() {
37 "checkbox" => {
38 match input.checked() {
39 true => "true".to_string(),
40 false => "false".to_string(),
41 }
42 },
43 _ => {
44 input.value()
45 }
46 }
47 })
48 .or_else(|| {
49 target
50 .dyn_ref()
51 .map(|input: &web_sys::HtmlTextAreaElement| input.value())
52 })
53 .or_else(|| {
55 target
56 .dyn_ref()
57 .map(|input: &web_sys::HtmlSelectElement| input.value())
58 })
59 .or_else(|| {
60 target
61 .dyn_ref::<web_sys::HtmlElement>()
62 .unwrap()
63 .text_content()
64 })
65 .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener")
66 }
67
68 fn values(&self) -> Vec<(String, FormValue)> {
69 let mut values = Vec::new();
70
71 if let Some(form) = self.element.dyn_ref::<web_sys::HtmlFormElement>() {
73 let form_data = web_sys::FormData::new_with_form(form).unwrap();
74
75 for entry in form_data.entries().into_iter().flatten() {
76 if let Ok(array) = entry.dyn_into::<Array>() {
77 if let Some(name) = array.get(0).as_string() {
78 let value = array.get(1);
79 if let Some(file) = value.dyn_ref::<web_sys::File>() {
80 if file.name().is_empty() {
81 values.push((name, FormValue::File(None)));
82 } else {
83 let data =
84 WebFileData::new(file.clone(), FileReader::new().unwrap());
85 let as_file = FileData::new(data);
86
87 values.push((name, FormValue::File(Some(as_file))));
88 }
89 } else if let Some(s) = value.as_string() {
90 values.push((name, FormValue::Text(s)));
91 }
92 }
93 }
94 }
95 } else if let Some(select) = self.element.dyn_ref::<web_sys::HtmlSelectElement>() {
96 let options = get_select_data(select);
98 for option in &options {
99 values.push((select.name(), FormValue::Text(option.clone())));
100 }
101 }
102
103 values
104 }
105
106 fn as_any(&self) -> &dyn Any {
107 &self.event as &dyn Any
108 }
109
110 fn valid(&self) -> bool {
111 self.event
112 .target()
113 .and_then(|t| t.dyn_into::<web_sys::HtmlInputElement>().ok())
114 .map(|input| input.check_validity())
115 .unwrap_or(true)
116 }
117}
118
119impl HasFileData for WebFormData {
120 fn files(&self) -> Vec<FileData> {
121 use wasm_bindgen::JsCast;
122 self.event
123 .target()
124 .and_then(|t| t.dyn_into::<web_sys::HtmlInputElement>().ok())
125 .and_then(|input| input.files())
126 .map(crate::files::WebFileEngine::new)
127 .map(|engine| engine.to_files())
128 .unwrap_or_default()
129 }
130}
131
132#[wasm_bindgen(inline_js = r#"
134export function get_select_data(select) {
135 let values = [];
136 for (let i = 0; i < select.options.length; i++) {
137 let option = select.options[i];
138 if (option.selected) {
139 values.push(option.value.toString());
140 }
141 }
142
143 return values;
144}
145"#)]
146extern "C" {
147 fn get_select_data(select: &web_sys::HtmlSelectElement) -> Vec<String>;
148}