Merge pull request #147 from DioxusLabs/jk/form-ma
Pass form values as a hashmap on `oninput`/`onsubmit`
This commit is contained in:
5 files changed
+103
-11
No files matched your search
@@ -0,0 +1,26 @@
|
||||
//! Forms
|
||||
//!
|
||||
//! Dioxus forms deviate slightly from html, automatically returning all named inputs
|
||||
//! in the "values" field
|
||||
|
||||
use dioxus::prelude::*;
|
||||
|
||||
fn main() {
|
||||
dioxus::desktop::launch(app);
|
||||
}
|
||||
|
||||
fn app(cx: Scope) -> Element {
|
||||
cx.render(rsx! {
|
||||
div {
|
||||
h1 { "Form" }
|
||||
form {
|
||||
onsubmit: move |ev| println!("Submitted {:?}", ev.values),
|
||||
oninput: move |ev| println!("Input {:?}", ev.values),
|
||||
input { r#type: "text", name: "username" }
|
||||
input { r#type: "text", name: "full-name" }
|
||||
input { r#type: "password", name: "password" }
|
||||
button { "Submit the form" }
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -3,6 +3,8 @@ use dioxus_core::exports::bumpalo;
|
||||
use dioxus_core::*;
|
||||
|
||||
pub mod on {
|
||||
use std::collections::HashMap;
|
||||
|
||||
use super::*;
|
||||
macro_rules! event_directory {
|
||||
( $(
|
||||
@@ -484,6 +486,7 @@ pub mod on {
|
||||
#[derive(Debug)]
|
||||
pub struct FormData {
|
||||
pub value: String,
|
||||
pub values: HashMap<String, String>,
|
||||
/* DOMEvent: Send + SyncTarget relatedTarget */
|
||||
}
|
||||
|
||||
|
||||
@@ -216,7 +216,12 @@ export class Interpreter {
|
||||
}
|
||||
}
|
||||
// walk the tree to find the real element
|
||||
while (realId == null && target.parentElement != null) {
|
||||
while (realId == null) {
|
||||
// we've reached the root we don't want to send an event
|
||||
if (target.parentElement === null) {
|
||||
return;
|
||||
}
|
||||
|
||||
target = target.parentElement;
|
||||
realId = target.getAttribute(`data-dioxus-id`);
|
||||
}
|
||||
@@ -224,13 +229,33 @@ export class Interpreter {
|
||||
shouldPreventDefault = target.getAttribute(
|
||||
`dioxus-prevent-default`
|
||||
);
|
||||
|
||||
let contents = serialize_event(event);
|
||||
|
||||
if (shouldPreventDefault === `on${event.type}`) {
|
||||
// event.preventDefault();
|
||||
event.preventDefault();
|
||||
}
|
||||
if (event.type == "submit") {
|
||||
// event.preventDefault();
|
||||
event.preventDefault();
|
||||
}
|
||||
|
||||
if (target.tagName == "FORM") {
|
||||
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 {
|
||||
// @ts-ignore
|
||||
contents.values[name] =
|
||||
element.value ?? element.textContent;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (realId == null) {
|
||||
return;
|
||||
}
|
||||
@@ -255,7 +280,8 @@ export class Interpreter {
|
||||
}
|
||||
}
|
||||
}
|
||||
function serialize_event(event) {
|
||||
|
||||
export function serialize_event(event) {
|
||||
switch (event.type) {
|
||||
case "copy":
|
||||
case "cut":
|
||||
@@ -326,6 +352,7 @@ function serialize_event(event) {
|
||||
}
|
||||
return {
|
||||
value: value,
|
||||
values: {},
|
||||
};
|
||||
}
|
||||
case "click":
|
||||
|
||||
@@ -42,6 +42,7 @@ features = [
|
||||
"HtmlInputElement",
|
||||
"HtmlSelectElement",
|
||||
"HtmlTextAreaElement",
|
||||
"HtmlFormElement",
|
||||
"EventTarget",
|
||||
"HtmlCollection",
|
||||
"Node",
|
||||
|
||||
+42
-7
@@ -41,7 +41,7 @@ impl WebsysDom {
|
||||
Some(Ok(id)) => {
|
||||
break Ok(UserEvent {
|
||||
name: event_name_from_typ(&typ),
|
||||
data: virtual_event_from_websys_event(event.clone()),
|
||||
data: virtual_event_from_websys_event(event.clone(), target.clone()),
|
||||
element: Some(ElementId(id)),
|
||||
scope_id: None,
|
||||
priority: dioxus_core::EventPriority::Medium,
|
||||
@@ -57,7 +57,10 @@ impl WebsysDom {
|
||||
} else {
|
||||
break Ok(UserEvent {
|
||||
name: event_name_from_typ(&typ),
|
||||
data: virtual_event_from_websys_event(event.clone()),
|
||||
data: virtual_event_from_websys_event(
|
||||
event.clone(),
|
||||
target.clone(),
|
||||
),
|
||||
element: None,
|
||||
scope_id: None,
|
||||
priority: dioxus_core::EventPriority::Low,
|
||||
@@ -144,7 +147,10 @@ unsafe impl Sync for DioxusWebsysEvent {}
|
||||
|
||||
// todo: some of these events are being casted to the wrong event type.
|
||||
// We need tests that simulate clicks/etc and make sure every event type works.
|
||||
fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send + Sync> {
|
||||
fn virtual_event_from_websys_event(
|
||||
event: web_sys::Event,
|
||||
target: Element,
|
||||
) -> Arc<dyn Any + Send + Sync> {
|
||||
use dioxus_html::on::*;
|
||||
use dioxus_html::KeyCode;
|
||||
|
||||
@@ -177,9 +183,6 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
||||
// todo: these handlers might get really slow if the input box gets large and allocation pressure is heavy
|
||||
// don't have a good solution with the serialized event problem
|
||||
"change" | "input" | "invalid" | "reset" | "submit" => {
|
||||
let evt: &web_sys::Event = event.dyn_ref().unwrap();
|
||||
|
||||
let target: web_sys::EventTarget = evt.target().unwrap();
|
||||
let value: String = (&target)
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlInputElement| {
|
||||
@@ -215,7 +218,38 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
||||
})
|
||||
.expect("only an InputElement or TextAreaElement or an element with contenteditable=true can have an oninput event listener");
|
||||
|
||||
Arc::new(FormData { value })
|
||||
let mut values = std::collections::HashMap::new();
|
||||
|
||||
// try to fill in form values
|
||||
if let Some(form) = target.dyn_ref::<web_sys::HtmlFormElement>() {
|
||||
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: String = (&element)
|
||||
.dyn_ref()
|
||||
.map(|input: &web_sys::HtmlInputElement| {
|
||||
match input.type_().as_str() {
|
||||
"checkbox" => {
|
||||
match input.checked() {
|
||||
true => "true".to_string(),
|
||||
false => "false".to_string(),
|
||||
}
|
||||
},
|
||||
_ => input.value()
|
||||
}
|
||||
})
|
||||
.or_else(|| target.dyn_ref().map(|input: &web_sys::HtmlTextAreaElement| input.value()))
|
||||
.or_else(|| target.dyn_ref().map(|input: &web_sys::HtmlSelectElement| input.value()))
|
||||
.or_else(|| target.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");
|
||||
|
||||
values.insert(name, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Arc::new(FormData { value, values })
|
||||
}
|
||||
"click" | "contextmenu" | "doubleclick" | "drag" | "dragend" | "dragenter" | "dragexit"
|
||||
| "dragleave" | "dragover" | "dragstart" | "drop" | "mousedown" | "mouseenter"
|
||||
@@ -307,6 +341,7 @@ fn virtual_event_from_websys_event(event: web_sys::Event) -> Arc<dyn Any + Send
|
||||
| "playing" | "progress" | "ratechange" | "seeked" | "seeking" | "stalled" | "suspend"
|
||||
| "timeupdate" | "volumechange" | "waiting" => Arc::new(MediaData {}),
|
||||
"toggle" => Arc::new(ToggleData {}),
|
||||
|
||||
_ => Arc::new(()),
|
||||
}
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user