浏览代码

Merge pull request #147 from DioxusLabs/jk/form-ma

Pass form values as a hashmap on `oninput`/`onsubmit`
Jonathan Kelley 3 年之前
父节点
当前提交
5383a661c9
共有 5 个文件被更改,包括 103 次插入11 次删除
  1. 26 0
      examples/form.rs
  2. 3 0
      packages/html/src/events.rs
  3. 31 4
      packages/interpreter/src/interpreter.js
  4. 1 0
      packages/web/Cargo.toml
  5. 42 7
      packages/web/src/dom.rs

+ 26 - 0
examples/form.rs

@@ -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 - 0
packages/html/src/events.rs

@@ -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 */
     }
 

+ 31 - 4
packages/interpreter/src/interpreter.js

@@ -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":

+ 1 - 0
packages/web/Cargo.toml

@@ -42,6 +42,7 @@ features = [
     "HtmlInputElement",
     "HtmlSelectElement",
     "HtmlTextAreaElement",
+    "HtmlFormElement",
     "EventTarget",
     "HtmlCollection",
     "Node",

+ 42 - 7
packages/web/src/dom.rs

@@ -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(()),
     }
 }