The DynamicSlot Component
The previous subchapter covered dynamically updating HTML elements. Now we'll be adding and removing elements.
The DynamicSlot
component
provided by Async UI acts like a "slot".
You can dynamically set what Future runs in the slot.
In this example, we will display a button for 3 seconds, display some text for 3 seconds, and then display nothing.
#![allow(unused)] fn main() { use async_ui_web::{ components::DynamicSlot, html::Button, join, shortcut_traits::ShortcutRenderStr, }; use futures_lite::FutureExt; // for .boxed_local(), which converts a Future to a `Box<dyn Future>` use gloo_timers::future::TimeoutFuture; // nice async interface to `setTimeout` async fn show_button_and_remove() { let slot = DynamicSlot::new(); slot.set_future( // put <button>I will disappear soon!</button> in the slot Button::new() .render("I will disappear soon!".render()) .boxed_local(), // make it dynamically typed so we can put Futures of other types in the slot ); // join two Futures: // * one to render the slot // * the other to manipulate the content of the slot join(( slot.render(), // render the slot async { // wait 3 seconds TimeoutFuture::new(3000).await; // 👇 replace the button in the slot with a text slot.set_future("The button is gone!".render().boxed_local()); // wait another 3 seconds TimeoutFuture::new(3000).await; // 👇 remove the text in the slot slot.clear_future(); }, )) .await; } }
It's not magic
If you're familiar with hand-implementing Futures, take a look at the source code of DynamicSlot.
You'll see that it's no private-API-powered magic; it's just general async Rust code. You can even implement it yourself!
Extra
Can you implement the example above without DynamicSlot
?
Hint: there is race
,
which is like join
, but completes as soon as the first Future completes.
Click to view solution
Let's make a helper function first
#![allow(unused)] fn main() { use async_ui_web::race; // 👈 new! use std::future::Future; /// Run the given Future. /// If it is still running after 3 seconds, just drop it and return. async fn run_for_3_seconds(f: impl Future<Output = ()>) { race(( f, // the Future to run TimeoutFuture::new(3000), // a Future that waits 3000 ms )) .await } }
And now our main UI
#![allow(unused)] fn main() { async fn show_button_and_remove_2() { run_for_3_seconds( // show <button>I will disappear soon!</button> Button::new().render("I will disappear soon!".render()), ) .await; run_for_3_seconds( // the text "The button is gone!".render(), ) .await; } }