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

How do I attach a function to window? #230

Closed
WestXu opened this issue Aug 16, 2021 · 10 comments
Closed

How do I attach a function to window? #230

WestXu opened this issue Aug 16, 2021 · 10 comments
Labels
need info Further information is requested

Comments

@WestXu
Copy link

WestXu commented Aug 16, 2021

Newbie here.

Previously I use wasm-pack doing this:

<script type="module">
    console.log("Initializing wasm...")
    import init, { my_set_panic_hook, my_function, MyStruct } from './pkg/my_project.js';
    await init();
    my_set_panic_hook();
    console.log("Initialized.");

    window.my_function= my_function; 👈
    window.MyStruct = MyStruct; 👈
</script>
<script>
    window.my_function(); 👈
    window.MyStruct.new(); 👈
</script>

What's the equivalent pub fn main() using trunk?

And if this is not the right way to call wasm function from js, what is?

@lukechu10
Copy link
Contributor

Currently there isn't a way to access the generated wasm manually. Trunk automatically injects that first script tag into the generated html file.

Instead, you can set window.my_function and window.MyStruct from inside WASM by using web_sys and js_sys::Reflect

@WestXu
Copy link
Author

WestXu commented Aug 19, 2021

Currently there isn't a way to access the generated wasm manually. Trunk automatically injects that first script tag into the generated html file.

Instead, you can set window.my_function and window.MyStruct from inside WASM by using web_sys and js_sys::Reflect

Is there any snippets your can share on this?

I tried this:

use js_sys::Reflect;
use wasm_bindgen::prelude::*;

#[wasm_bindgen]
pub fn my_function() -> Result<(), JsValue> {
    Ok(())
}

fn main() {
    let window = web_sys::window().expect("no global `window` exists");

    Reflect::set(&window, &"my_function".into(), &my_function.into())
        .expect("Failed reflecting my_function.");
}

But I got:

error[E0277]: the trait bound `wasm_bindgen::JsValue: From<fn() -> Result<(), wasm_bindgen::JsValue> {my_function}>` is not satisfied
  --> src/main.rs:17:63
   |
17 |     Reflect::set(&window, &"my_function".into(), &my_function.into())
   |                                                               ^^^^ the trait `From<fn() -> Result<(), wasm_bindgen::JsValue> {my_function}>` is not implemented for `wasm_bindgen::JsValue`
   |
   = help: the following implementations were found:
             <wasm_bindgen::JsValue as From<&'a String>>
             <wasm_bindgen::JsValue as From<&'a T>>
             <wasm_bindgen::JsValue as From<&'a str>>
             <wasm_bindgen::JsValue as From<Array>>
           and 70 others
   = note: required because of the requirements on the impl of `Into<wasm_bindgen::JsValue>` for `fn() -> Result<(), wasm_bindgen::JsValue> {my_function}`

error: aborting due to previous error; 1 warning emitted

@lukechu10
Copy link
Contributor

Ah yes, you must create a Closure instead and cast it into a JsValue. There are docs on how to do that here: https://docs.rs/wasm-bindgen/0.2.75/wasm_bindgen/closure/struct.Closure.html

@thedodd
Copy link
Member

thedodd commented Aug 20, 2021

@WestXu looks like you are probably in need of something like this: #184 Which allows customization of the app's init script.

@thedodd
Copy link
Member

thedodd commented Aug 20, 2021

Otherwise, you could use the snippets system (https://trunkrs.dev/assets/#js-snippets) and pass along a Rust/WASM function for the JS function to use.

Not 100% sure what your requirements are. Perhaps describing what your ultimate goal is will help folks to find a viable solution.

@thedodd thedodd added the need info Further information is requested label Aug 20, 2021
@WestXu
Copy link
Author

WestXu commented Aug 20, 2021

Ah yes, you must create a Closure instead and cast it into a JsValue. There are docs on how to do that here: https://docs.rs/wasm-bindgen/0.2.75/wasm_bindgen/closure/struct.Closure.html

Wow I literally don't understand a single word in this pile of whatever:

Closure::wrap(Box::new(|| {
    web_sys::console::log_1(&"inverval elapsed!".into());
}) as Box<dyn FnMut()>).as_ref().unchecked_ref(),;

I eventually go with this:

#[wasm_bindgen]
pub struct Wrapper {}

#[wasm_bindgen]
impl Wrapper {
    #[wasm_bindgen]
    pub fn my_function(&self) -> Result<(), JsValue> {
        Ok(())
    }
}

fn main() {
    Reflect::set(
        &web_sys::window().unwrap(),
        &JsValue::from("wrapper"),
        &JsValue::from(Wrapper {}),
    )
    .unwrap();
}

And use it like this:

<script>
    window.wrapper.my_function();
</script>

Yea, as long as it works.

@WestXu
Copy link
Author

WestXu commented Aug 20, 2021

@WestXu looks like you are probably in need of something like this: #184 Which allows customization of the app's init script.

This is definitely helpful.

@thedodd
Copy link
Member

thedodd commented Aug 20, 2021

@WestXu yea, until browsers start exposing a more direct WASM ABI with something similar to WASI, the browser ABI will have this rough FFI feel to it. Lots of frameworks offer a slightly nicer pattern on top of this, but ultimately you are passing a boxed fn as a closure.

@WestXu
Copy link
Author

WestXu commented Aug 22, 2021

@WestXu yea, until browsers start exposing a more direct WASM ABI with something similar to WASI, the browser ABI will have this rough FFI feel to it. Lots of frameworks offer a slightly nicer pattern on top of this, but ultimately you are passing a boxed fn as a closure.

I won't blame it though. This project is amazing already. I'm happy with what it is doing now.

Feel free to close it.

@thedodd
Copy link
Member

thedodd commented Aug 25, 2021

Sounds good! Thanks for the discussion and sharing your findings.

@thedodd thedodd closed this as completed Aug 25, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
need info Further information is requested
Projects
None yet
Development

No branches or pull requests

3 participants