From f1f7517b888e9e77e612d656ee0c876717c799af Mon Sep 17 00:00:00 2001 From: Evan Almloff Date: Thu, 6 Apr 2023 11:11:08 -0500 Subject: [PATCH] Fix form events with select multiple --- packages/html/src/events/form.rs | 2 +- packages/interpreter/src/interpreter.js | 41 +++++++---------- packages/web/src/dom.rs | 59 ++++++++++++------------- 3 files changed, 45 insertions(+), 57 deletions(-) diff --git a/packages/html/src/events/form.rs b/packages/html/src/events/form.rs index 361ebe2d4..6edf60448 100644 --- a/packages/html/src/events/form.rs +++ b/packages/html/src/events/form.rs @@ -10,7 +10,7 @@ pub type FormEvent = Event; pub struct FormData { pub value: String, - pub values: HashMap, + pub values: HashMap>, #[cfg_attr(feature = "serialize", serde(skip))] pub files: Option>, diff --git a/packages/interpreter/src/interpreter.js b/packages/interpreter/src/interpreter.js index b921c7c22..653eb15fb 100644 --- a/packages/interpreter/src/interpreter.js +++ b/packages/interpreter/src/interpreter.js @@ -17,8 +17,7 @@ class ListenerMap { } else { this.global[event_name].active++; } - } - else { + } else { const id = element.getAttribute("data-dioxus-id"); if (!this.local[id]) { this.local[id] = {}; @@ -32,11 +31,13 @@ class ListenerMap { if (bubbles) { this.global[event_name].active--; if (this.global[event_name].active === 0) { - this.root.removeEventListener(event_name, this.global[event_name].callback); + this.root.removeEventListener( + event_name, + this.global[event_name].callback + ); delete this.global[event_name]; } - } - else { + } else { const id = element.getAttribute("data-dioxus-id"); delete this.local[id][event_name]; if (this.local[id].length === 0) { @@ -143,8 +144,7 @@ class Interpreter { SetAttribute(id, field, value, ns) { if (value === null) { this.RemoveAttribute(id, field, ns); - } - else { + } else { const node = this.nodes[id]; this.SetAttributeInner(node, field, value, ns); } @@ -342,7 +342,6 @@ class Interpreter { this.RemoveEventListener(edit.id, edit.name); break; case "NewEventListener": - let bubbles = event_bubbles(edit.name); // this handler is only provided on desktop implementations since this @@ -360,7 +359,10 @@ class Interpreter { let a_element = target.closest("a"); if (a_element != null) { event.preventDefault(); - if (shouldPreventDefault !== `onclick` && a_element.getAttribute(`dioxus-prevent-default`) !== `onclick`) { + if ( + shouldPreventDefault !== `onclick` && + a_element.getAttribute(`dioxus-prevent-default`) !== `onclick` + ) { const href = a_element.getAttribute("href"); if (href !== "" && href !== null && href !== undefined) { window.ipc.postMessage( @@ -404,23 +406,10 @@ class Interpreter { target.tagName === "FORM" && (event.type === "submit" || event.type === "input") ) { - for (let x = 0; x < target.elements.length; x++) { - let element = target.elements[x]; - let name = element.getAttribute("name"); - if (name != null) { - if (element.getAttribute("type") === "checkbox") { - // @ts-ignore - contents.values[name] = element.checked ? "true" : "false"; - } else if (element.getAttribute("type") === "radio") { - if (element.checked) { - contents.values[name] = element.value; - } - } else { - // @ts-ignore - contents.values[name] = - element.value ?? element.textContent; - } - } + const formData = new FormData(target); + + for (let name of formData.keys()) { + contents.values[name] = formData.getAll(name); } } diff --git a/packages/web/src/dom.rs b/packages/web/src/dom.rs index 4d17749d8..5b56e663e 100644 --- a/packages/web/src/dom.rs +++ b/packages/web/src/dom.rs @@ -13,9 +13,10 @@ use dioxus_core::{ use dioxus_html::{event_bubbles, CompositionData, FormData}; use dioxus_interpreter_js::{save_template, Channel}; use futures_channel::mpsc; +use js_sys::Array; use rustc_hash::FxHashMap; use std::{any::Any, rc::Rc}; -use wasm_bindgen::{closure::Closure, JsCast}; +use wasm_bindgen::{closure::Closure, prelude::wasm_bindgen, JsCast}; use web_sys::{Document, Element, Event, HtmlElement}; use crate::Config; @@ -325,35 +326,16 @@ fn read_input_to_data(target: Element) -> Rc { // try to fill in form values if let Some(form) = target.dyn_ref::() { - let elements = form.elements(); - for x in 0..elements.length() { - let element = elements.item(x).unwrap(); - if let Some(name) = element.get_attribute("name") { - let value: Option = element - .dyn_ref() - .map(|input: &web_sys::HtmlInputElement| { - match input.type_().as_str() { - "checkbox" => { - match input.checked() { - true => Some("true".to_string()), - false => Some("false".to_string()), - } - }, - "radio" => { - match input.checked() { - true => Some(input.value()), - false => None, - } - } - _ => Some(input.value()) - } - }) - .or_else(|| element.dyn_ref().map(|input: &web_sys::HtmlTextAreaElement| Some(input.value()))) - .or_else(|| element.dyn_ref().map(|input: &web_sys::HtmlSelectElement| Some(input.value()))) - .or_else(|| Some(element.dyn_ref::().unwrap().text_content())) - .expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener"); - if let Some(value) = value { - values.insert(name, value); + let form_data = get_form_data(form); + for value in form_data.entries().into_iter().flatten() { + 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 = + item_values.iter().filter_map(|v| v.as_string()).collect(); + + values.insert(name, item_values); + } } } } @@ -366,6 +348,23 @@ fn read_input_to_data(target: Element) -> Rc { }) } +// web-sys does not expose the keys api for form data, so we need to manually bind to it +#[wasm_bindgen(inline_js = r#" + export function get_form_data(form) { + let values = new Map(); + const formData = new FormData(form); + + for (let name of formData.keys()) { + values.set(name, formData.getAll(name)); + } + + return values; + } +"#)] +extern "C" { + fn get_form_data(form: &web_sys::HtmlFormElement) -> js_sys::Map; +} + fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Element)> { let mut target = event .target()