#Error when calling HtmlElement.click()

8 messages · Page 1 of 1 (latest)

wanton gust
#

Hi there! I'm running into some issues when trying to set a payload on a hidden anchor element and downloading it by triggering the "click" method like the snippet below. Even though I haven't set any "onclick" closure myself on the hidden anchor element, I'm still running into Uncaught Error: closure invoked recursively or after being dropped

use wasm_bindgen::JsValue;
use web_sys::HtmlElement;

pub fn save_json(el: &HtmlElement, filename: &str, content: &str) -> Result<(), JsValue> {
    let data = format!("data:text/json;charset=utf-8,{}", content);
    el.set_attribute("href", data.as_str())?;
    el.set_attribute("download", format!("{}.json", filename).as_str())?;
    el.click(); // <-- the offending line. It simulates a click such that the browser downloads the file (when a completely different button is clicked, actually)
    el.remove_attribute("href")?;
    el.remove_attribute("download")?;
    Ok(())
}

Any ideas on how to safely trigger an event method on an HtmlElement?

astral quiver
#

the problem you have is not in this code

#

the error is about some Closure object

#

none of the code you have shared involves any closures at all

wanton gust
#

OK, I tried to extract the parts of the Yew components where it is used:

use gloo_console::debug;
use schemars::JsonSchema;
use serde::{Deserialize, Serialize};
use serde_json::to_string;
use wasm_bindgen::JsValue;
use web_sys::{HtmlElement, HtmlInputElement};
use yew::prelude::*;

// fn save_json(...)


#[derive(Clone, Debug, Default, Serialize, Deserialize, JsonSchema)]
pub struct Foo {
    bar: usize,
}

#[function_component]
pub fn DummyModal() -> Html {
    let input_ref = use_node_ref();
    let anchor_ref = use_node_ref();
    let filename: UseStateHandle<Option<String>> = use_state(|| None);

    let oninput = {
        let input_ref = input_ref.clone();
        let filename = filename.clone();
        Callback::from(move |e: InputEvent| {
            e.stop_propagation();
            let el = input_ref.cast::<HtmlInputElement>().unwrap();
            let value = el.value().parse::<String>().unwrap();
            filename.set(Some(value));
        })
    };

    let onclick = {
        let filename = filename.clone();
        let anchor_ref = anchor_ref.clone();
        Callback::from(move |e: MouseEvent| {
            e.stop_propagation();
            let el = anchor_ref.cast::<HtmlElement>().unwrap();
            debug!("exporting...");
            let content = to_string(&Foo::default()).unwrap();
            let filename = (&*filename).clone().unwrap_or_else(|| "foo".to_string());
            save_json(&el, filename.as_str(), content.as_str()).unwrap();
        })
    };

    html! {
        <div>
            <label for="exportFileName">{"Filename"}</label>
            <input
                id="exportFileName"
                ref={input_ref}
                type="text"
                placeholder={("filename").to_owned()}
                {oninput}
            />
            <button type="button" {onclick}>{"Save"}</button>
            <a style={"display: none;"} ref={anchor_ref}/>
        </div>
    }
}
#

Where the full error would be:

Uncaught Error Error: closure invoked recursively or after being dropped
    at imports.wbg.__wbindgen_throw (localhost꞉8080/canopy-rs.js:935:15)
    at $wasm_bindgen::throw_str::h0f054f8ba1702a54 (localhost꞉8080/canopy-rs_bg.wasm:1:6755779)
    at $<dyn core::ops::function::FnMut<(&A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h3c7f39777de590ae (localhost꞉8080/canopy-rs_bg.wasm:1:5018513)
    at __wbg_adapter_46 (localhost꞉8080/canopy-rs.js:253:14)
    at real (localhost꞉8080/canopy-rs.js:226:20)
    at imports.wbg.__wbg_click_f3e37a4a2595187d (localhost꞉8080/canopy-rs.js:693:25)
    at $web_sys::features::gen_HtmlElement::HtmlElement::click::hfbcf3cbc72d0be9e (localhost꞉8080/canopy-rs_bg.wasm:1:6755434)
    at $canopy_rs::modal::dummy::save_json::haffca16fc1e1cbfd (localhost꞉8080/canopy-rs_bg.wasm:1:2097534)
    at $<canopy_rs::modal::dummy::DummyModal as yew::functional::FunctionProvider>::run::inner::{{closure}}::h6477614f926f2fc7 (localhost꞉8080/canopy-rs_bg.wasm:1:3266255)
...
#
    at $yew::callback::Callback<IN,OUT>::emit::h0f0faa462e460ffb (localhost꞉8080/canopy-rs_bg.wasm:1:5614490)
    at $<yew::html::listener::events::onclick::Wrapper as yew::virtual_dom::listeners::Listener>::handle::hde33f522999feb0f (localhost꞉8080/canopy-rs_bg.wasm:1:6710755)
    at $yew::dom_bundle::btag::listeners::Registry::get_handler::{{closure}}::h3a57a1c9fe5def15 (localhost꞉8080/canopy-rs_bg.wasm:1:3528837)
    at $yew::dom_bundle::subtree_root::SubtreeData::handle::{{closure}}::h39493a25b3e1219f (localhost꞉8080/canopy-rs_bg.wasm:1:3605429)
    at $yew::dom_bundle::subtree_root::SubtreeData::handle::h9c135eef9a3b43ae (localhost꞉8080/canopy-rs_bg.wasm:1:2370942)
    at $yew::dom_bundle::subtree_root::SubtreeData::add_listener::{{closure}}::hf96544503104e361 (localhost꞉8080/canopy-rs_bg.wasm:1:5946358)
    at $<dyn core::ops::function::FnMut<(&A,)>+Output = R as wasm_bindgen::closure::WasmClosure>::describe::invoke::h3c7f39777de590ae (localhost꞉8080/canopy-rs_bg.wasm:1:5018681)
    at __wbg_adapter_46 (localhost꞉8080/canopy-rs.js:253:14)
    at real (localhost꞉8080/canopy-rs.js:226:20)
wanton gust
# astral quiver none of the code you have shared involves any closures at all

Hence my question. I'm porting a TypeScript/Vue app to Yew and I used to call the element.click() method there without any problem, since almost anything goes in JS for better or worse...
If I search for my problem, most solutions boil down to calling "forget" on some closure, but since I didn't add a Closure myself (and rely on default browser behavior AFAIK), it's hard to forget it.