Lists

We've so far covered how to render many Futures (we rendered 100 <span>s in chapter 1), and how to dynamically put or remove a Future (the previous subchapter on DynamicSlot).

Now, how do we dynamically insert or remove from a list of many Futures?

Async UI comes with many different list components. They are documented here.

The most recommended list - the one we will use in this guide - is ModeledList.

Each ModeledList has two parts:

  • The ModeledList instance. This is what you .render(). It knows how to turn each list item into a Future to be rendered.
  • The data model: ListModel. This is where the items of the list actually live. It is similar to a Vec.

Operation is simple: you update the ListModel, then tell the ModeledList that the data have been updated, and the ModeledList will figure out inserting new items and deleting removed items.

Example: a Fibonacci list

We'll make a list of Fibonacci numbers. First, let's import things

#![allow(unused)]
fn main() {
use async_ui_web::{
    html::Div,
    join,
    lists::{ListModel, ModeledList}, // 👈 new!
    shortcut_traits::ShortcutRenderStr,
};
use gloo_timers::future::TimeoutFuture;
}

Before making the list, let's think of how we'll render each item in the list

#![allow(unused)]
fn main() {
async fn render_one_item(n: usize, fib_n: u64) {
    Div::new()
        .render(format!("The {n}th Fibonacci number is {fib_n}.").render())
        .await;
}
}

And finally, we make the list

#![allow(unused)]
fn main() {
async fn fibonacci() {
    // 👇 create a list that can render numbers
    let list = ModeledList::new(|(n, fib_n)| render_one_item(*n, *fib_n));
    // 👇 create a model that contains the numbers we'll render
    let mut fibo = ListModel::from(vec![
        (1, 1), // fib_1 is 1
        (2, 1), // fib_2 is also 1
    ]);
    // 👇 tell the list to render the numbers in the `fibo` model
    list.update(&fibo);

    // join 2 Futures:
    // * the list
    // * a Future to manipulate the items
    join((
        list.render(), // 👈 render the list
        async {
            loop {
                // wait 1 second
                TimeoutFuture::new(1000).await;

                // 👇 change `fibo`, adding the next fibonacci number
                fibo.push((
                    fibo.len() + 1, // `n` - the index of the next fibo number
                    // compute `fib_n`
                    fibo.iter()
                        .rev()
                        .map(|(_n, fib_n)| fib_n)
                        .take(2)
                        .cloned()
                        .sum(),
                ));
                // 👇 tell the list that the numbers in the model have changed
                list.update(&fibo);
            }
        },
    ))
    .await;
}
}

A webpage with a list of Fibonacci numbers

Warning: Our Fibonacci implementation will eventually overflow.

Fixing the incorrect English usage ("1th", "2th", "3th") is left as an exercise for the reader.