Fix form events with select multiple

This commit is contained in:
Evan Almloff committed 2023-04-06 11:11:08 -05:00
1 parent 61d100e1f8
commit f1f7517b88
3 files changed
+45 -57

No files matched your search

+1 -1
View File
@@ -10,7 +10,7 @@ pub type FormEvent = Event<FormData>;
pub struct FormData { pub struct FormData {
pub value: String, pub value: String,
pub values: HashMap<String, String>, pub values: HashMap<String, Vec<String>>,
#[cfg_attr(feature = "serialize", serde(skip))] #[cfg_attr(feature = "serialize", serde(skip))]
pub files: Option<std::sync::Arc<dyn FileEngine>>, pub files: Option<std::sync::Arc<dyn FileEngine>>,
+15 -26
View File
@@ -17,8 +17,7 @@ class ListenerMap {
} else { } else {
this.global[event_name].active++; this.global[event_name].active++;
} }
} } else {
else {
const id = element.getAttribute("data-dioxus-id"); const id = element.getAttribute("data-dioxus-id");
if (!this.local[id]) { if (!this.local[id]) {
this.local[id] = {}; this.local[id] = {};
@@ -32,11 +31,13 @@ class ListenerMap {
if (bubbles) { if (bubbles) {
this.global[event_name].active--; this.global[event_name].active--;
if (this.global[event_name].active === 0) { 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]; delete this.global[event_name];
} }
} } else {
else {
const id = element.getAttribute("data-dioxus-id"); const id = element.getAttribute("data-dioxus-id");
delete this.local[id][event_name]; delete this.local[id][event_name];
if (this.local[id].length === 0) { if (this.local[id].length === 0) {
@@ -143,8 +144,7 @@ class Interpreter {
SetAttribute(id, field, value, ns) { SetAttribute(id, field, value, ns) {
if (value === null) { if (value === null) {
this.RemoveAttribute(id, field, ns); this.RemoveAttribute(id, field, ns);
} } else {
else {
const node = this.nodes[id]; const node = this.nodes[id];
this.SetAttributeInner(node, field, value, ns); this.SetAttributeInner(node, field, value, ns);
} }
@@ -342,7 +342,6 @@ class Interpreter {
this.RemoveEventListener(edit.id, edit.name); this.RemoveEventListener(edit.id, edit.name);
break; break;
case "NewEventListener": case "NewEventListener":
let bubbles = event_bubbles(edit.name); let bubbles = event_bubbles(edit.name);
// this handler is only provided on desktop implementations since this // this handler is only provided on desktop implementations since this
@@ -360,7 +359,10 @@ class Interpreter {
let a_element = target.closest("a"); let a_element = target.closest("a");
if (a_element != null) { if (a_element != null) {
event.preventDefault(); 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"); const href = a_element.getAttribute("href");
if (href !== "" && href !== null && href !== undefined) { if (href !== "" && href !== null && href !== undefined) {
window.ipc.postMessage( window.ipc.postMessage(
@@ -404,23 +406,10 @@ class Interpreter {
target.tagName === "FORM" && target.tagName === "FORM" &&
(event.type === "submit" || event.type === "input") (event.type === "submit" || event.type === "input")
) { ) {
for (let x = 0; x < target.elements.length; x++) { const formData = new FormData(target);
let element = target.elements[x];
let name = element.getAttribute("name"); for (let name of formData.keys()) {
if (name != null) { contents.values[name] = formData.getAll(name);
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;
}
}
} }
} }
+29 -30
View File
@@ -13,9 +13,10 @@ use dioxus_core::{
use dioxus_html::{event_bubbles, CompositionData, FormData}; use dioxus_html::{event_bubbles, CompositionData, FormData};
use dioxus_interpreter_js::{save_template, Channel}; use dioxus_interpreter_js::{save_template, Channel};
use futures_channel::mpsc; use futures_channel::mpsc;
use js_sys::Array;
use rustc_hash::FxHashMap; use rustc_hash::FxHashMap;
use std::{any::Any, rc::Rc}; 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 web_sys::{Document, Element, Event, HtmlElement};
use crate::Config; use crate::Config;
@@ -325,35 +326,16 @@ fn read_input_to_data(target: Element) -> Rc<FormData> {
// try to fill in form values // try to fill in form values
if let Some(form) = target.dyn_ref::<web_sys::HtmlFormElement>() { if let Some(form) = target.dyn_ref::<web_sys::HtmlFormElement>() {
let elements = form.elements(); let form_data = get_form_data(form);
for x in 0..elements.length() { for value in form_data.entries().into_iter().flatten() {
let element = elements.item(x).unwrap(); if let Ok(array) = value.dyn_into::<Array>() {
if let Some(name) = element.get_attribute("name") { if let Some(name) = array.get(0).as_string() {
let value: Option<String> = element if let Ok(item_values) = array.get(1).dyn_into::<Array>() {
.dyn_ref() let item_values =
.map(|input: &web_sys::HtmlInputElement| { item_values.iter().filter_map(|v| v.as_string()).collect();
match input.type_().as_str() {
"checkbox" => { values.insert(name, item_values);
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::<web_sys::HtmlElement>().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);
} }
} }
} }
@@ -366,6 +348,23 @@ fn read_input_to_data(target: Element) -> Rc<FormData> {
}) })
} }
// 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)> { fn walk_event_for_id(event: &web_sys::Event) -> Option<(ElementId, web_sys::Element)> {
let mut target = event let mut target = event
.target() .target()