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;
}
}