Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Pass form values as a hashmap on oninput/onsubmit #147

Merged
merged 10 commits into from
Feb 5, 2022
26 changes: 26 additions & 0 deletions examples/form.rs
Original file line number Diff line number Diff line change
@@ -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 changes: 3 additions & 0 deletions packages/html/src/events.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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 {
( $(
Expand Down Expand Up @@ -484,6 +486,7 @@ pub mod on {
#[derive(Debug)]
pub struct FormData {
pub value: String,
pub values: HashMap<String, String>,
/* DOMEvent: Send + SyncTarget relatedTarget */
}

Expand Down
35 changes: 31 additions & 4 deletions packages/interpreter/src/interpreter.js
Original file line number Diff line number Diff line change
Expand Up @@ -216,21 +216,46 @@ 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`);
}

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;
}
Expand All @@ -255,7 +280,8 @@ export class Interpreter {
}
}
}
function serialize_event(event) {

export function serialize_event(event) {
switch (event.type) {
case "copy":
case "cut":
Expand Down Expand Up @@ -326,6 +352,7 @@ function serialize_event(event) {
}
return {
value: value,
values: {},
};
}
case "click":
Expand Down
1 change: 1 addition & 0 deletions packages/web/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@ features = [
"HtmlInputElement",
"HtmlSelectElement",
"HtmlTextAreaElement",
"HtmlFormElement",
"EventTarget",
"HtmlCollection",
"Node",
Expand Down
49 changes: 42 additions & 7 deletions packages/web/src/dom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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,
Expand Down Expand Up @@ -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;

Expand Down Expand Up @@ -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| {
Expand Down Expand Up @@ -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"
Expand Down Expand Up @@ -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(()),
}
}
Expand Down