Pattern for JS components which need a DOM object #561
-
To avoid rebuilding everything in Rust and leveraging the JS ecosystem, wasm-bindgen enables us to use many JS libraries in Rust only by adding very simple glue code. Some JS libraries, however, need access to specific parts of the DOM, e.g. fullcalendar, a popular calendar widget, or wavesurfer, an audio waveform renderer. Both these libraries require a container element to be present in the page's DOM on which they then act. Now this is all fine and their suggested approach using an event listener on the document works well enough with wasm-bindgen bindings to their objects if the container is unconditionally part of the page's DOM. However, the interaction with sycamore seems to be non-trivial if one constructs the DOM with effects. For instance, // in a custom component …
let calendar_visible = create_signal(cx, false);
view! {
cx,
// … some code
(if *calendar_visible.get() {
div(id = "cal_container")
} else { View::empty() })
} With this setup, the container is not part of the DOM unless we toggle our signal (e.g. by letting the user press a button). We can add an effect that tries to call the wasm-bindgen methods but if I understand correctly, there is no guarantee about the order of effects. So my question to the community: does anyone rely on such JS components that require a certain DOM element and has a good pattern/solution to the effects/callback mess due to the DOM elements being guarded by signals? To clarify the relation to #459: I read the answer there and the suggestion that complicated interaction should be avoided. However, the libraries I am talking about only take control of one very specific container element and the sycamore signals I'm using effectively only concern whether the container element is shown/present or not. For the sake of this discussion, assume that removing the container element does no harm and the JS will not conflict with anything. This discussion is primarily about running some creation method when adding an element to the DOM. |
Beta Was this translation helpful? Give feedback.
Replies: 1 comment 3 replies
-
The best solution, which also happens to be the simplest and most idiomatic is just to create a new component whose job is just to bind to the JS library and nothing else. For example, you could create a You can then just use this component inside your Also I'm not sure if you're aware but you can use |
Beta Was this translation helpful? Give feedback.
The best solution, which also happens to be the simplest and most idiomatic is just to create a new component whose job is just to bind to the JS library and nothing else. For example, you could create a
FullCalenderWidget
component that creates adiv
and calls the JS code.You can then just use this component inside your
if/else
instead of putting thediv
there.Also I'm not sure if you're aware but you can use
NodeRef
with.get_web_sys()
to get a raw handle to aweb_sys::Node
which you should be able to pass directly to your JS library. This is nicer to use and less error-prone than using HTML ids.