Skip to content

Commit

Permalink
Merge pull request #147 from DioxusLabs/jk/form-ma
Browse files Browse the repository at this point in the history
Pass form values as a hashmap on `oninput`/`onsubmit`
  • Loading branch information
jkelleyrtp authored Feb 5, 2022
2 parents 185fcdb + a939df6 commit 5383a66
Show file tree
Hide file tree
Showing 5 changed files with 103 additions and 11 deletions.
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

0 comments on commit 5383a66

Please sign in to comment.