leptos -> update the option text when the underlaying datastructure changes

68 views Asked by At

In the code section below there is a button which adds another option to the select, by using the let add_option, which internally uses the signal selectionOptions.update.

Once the button was pressed there is a 5th element, the 44l and it appears and can be selected but the drop-down text is not updated.

The info!("n[0]: {} {}", n[0].label, n[0].amount) in the add_option call is printed to the F12 console in the webbrowser with the 200 value though.

n[0]: h0rses 200

The print button also loggs the correct value:

h0rses 200

I'm probably not using the signals correctly but I don't know what is going wrong.

I have this code:

#[component]
pub fn DynSelector() -> impl IntoView {

    // type SelectionHolder = Vec<(usize, (ReadSignal<i32>, WriteSignal<i32>))>;

    // #[derive(Copy, Clone)]
    // struct SelectionUpdater {
    //     set_selectionOptions: WriteSignal<SelectionHolder>,
    // }
    #[derive(Debug, Clone, Copy)]
    pub struct SelectionOption {
        pub label: &'static str,
        pub id: u32,
        pub amount: u32,
    }
    let selectionOptions1: Vec<SelectionOption> = vec![
        SelectionOption{ label: "h0rses", id: 0, amount: 0},
        SelectionOption{ label: "b1rds", id: 1, amount: 10},
        SelectionOption{ label: "2nfish", id: 2, amount: 0},
        SelectionOption{ label: "3lk", id: 3, amount: 20},
    ];
    // let selectionOptions2: Vec<SelectionOption> = vec![
    //     SelectionOption{ label: "h0rses", id: 0, amount: 0},
    //     SelectionOption{ label: "b1rds", id: 1, amount: 10},
    //     SelectionOption{ label: "2nfish", id: 2, amount: 0},
    //     SelectionOption{ label: "3lk", id: 3, amount: 20},
    //     SelectionOption{ label: "4eel", id: 4, amount: 20},
    // ];
    let default_selection: RwSignal<Option<u32>> = create_rw_signal(Some(0));
    let selectionOptions = create_rw_signal::<Vec<SelectionOption>>(selectionOptions1);
    let print = move |_| {
      let s = selectionOptions.get();
      info!("{} {}", s[0].label, s[0].amount);
    };
    let add_option = move |_| {
        let a = SelectionOption{ label: "4eel", id: 4, amount: 20};
        selectionOptions.update(|n| {
            n[0].amount = 200;
            n.push(a);
            info!("n[0]: {} {}", n[0].label, n[0].amount)
        });
    };
    view! {
      <div style="background:#eae3ff">
        <select
          id = "mymultiselect"
          on:change = move |ev| {
            let target_value = event_target_value(&ev);
            if target_value.is_empty() {
              default_selection.set(None);
            } else {
               match target_value.parse() {
                 Ok(v) => {
                   info!("you selected {}", v);
                   default_selection.set(Some(v))
                 },
                 Err(_) => {
                   error!("Error: Unexpected option value {target_value}");
                 },
              }
            }
          }
        >
          <For
            each = move || selectionOptions.get()
            key = |option| option.id
            let:option
          >
            <option
              value = option.id
              default_selection = (default_selection.get() == Some(option.id))
            > {
               let v = format!("{} - {}", option.label, option.amount);
               v
              }
            </option>
          </For>
        </select>
        <p>
        "You selected: "
        <span data-testid="mymultiselection">{move || {
          let selected_option = default_selection.get();
            match selected_option {
                Some(v) => {
                    let l = selectionOptions.get()[v as usize].label;
                    let a = selectionOptions.get()[v as usize].amount;
                    format!("{} - {}", l, a.to_string())
                },
                None => "no idea...".to_string()
            }
        }
        }</span>
         <button on:click=add_option>
           "Add another option to selector"
         </button>
         <button on:click=print>
           "Print"
         </button>
        </p>
      </div>
    }
}
1

There are 1 answers

0
qknight On

The solution was this:

#[component]
pub fn DynSelector() -> impl IntoView {

    // type SelectionHolder = Vec<(usize, (ReadSignal<i32>, WriteSignal<i32>))>;

    // #[derive(Copy, Clone)]
    // struct SelectionUpdater {
    //     set_selectionOptions: WriteSignal<SelectionHolder>,
    // }
    #[derive(Debug, Clone, Copy)]
    pub struct SelectionOption {
        pub label: &'static str,
        pub id: u32,
        pub amount: RwSignal<u32>,
    }
    let selectionOptions1: Vec<SelectionOption> = vec![
        SelectionOption{ label: "h0rses", id: 0, amount: create_rw_signal::<u32>(0)},
        SelectionOption{ label: "b1rds", id: 1, amount: create_rw_signal::<u32>(10)},
        SelectionOption{ label: "2nfish", id: 2, amount: create_rw_signal::<u32>(20)},
        SelectionOption{ label: "3lk", id: 3, amount: create_rw_signal::<u32>(30)},
    ];
    let default_selection: RwSignal<Option<u32>> = create_rw_signal(Some(0));
    let selectionOptions = create_rw_signal::<Vec<SelectionOption>>(selectionOptions1);
    let print = move |_| {
        let s = selectionOptions.get();
        info!("{} {}", s[0].label, s[0].amount.get());
    };
    let add_option = move |_| {
        let a = SelectionOption{ label: "4eel", id: 4, amount: create_rw_signal::<u32>(20)};
        selectionOptions.update(|n| {
            if n.len() > 0 {
                n[0].amount.set(200);
                info!("n[0]: {} {}", n[0].label, n[0].amount.get())
            }
            n.push(a);
        });
    };
    view! {
      <div style="background:#eae3ff">
        <select
          id = "mymultiselect"
          on:change = move |ev| {
            let target_value = event_target_value(&ev);
            if target_value.is_empty() {
              default_selection.set(None);
            } else {
               match target_value.parse() {
                 Ok(v) => {
                   info!("you selected {}", v);
                   default_selection.set(Some(v))
                 },
                 Err(_) => {
                   error!("Error: Unexpected option value {target_value}");
                 },
              }
            }
          }
        >
          <For
            each = {move || selectionOptions.clone().get()}
            key = |option| option.id
            let:option
          >
            <option
              value = move || option.id
              default_selection = (default_selection.get() == Some(option.id))
            > { move || {
               let z = option.amount.get();
               let v = format!("{} - {}", option.label, z);
               v
               }
              }
            </option>
          </For>
        </select>
        <p>
        "You selected: "
        <span data-testid="mymultiselection">{move || {
          let selected_option = default_selection.get();
            match selected_option {
                Some(v) => {
                    let l = selectionOptions.get()[v as usize].label;
                    let a = selectionOptions.get()[v as usize].amount.get();
                    format!("{} - {}", l, a.to_string())
                },
                None => "no idea...".to_string()
            }
        }
        }</span>
         <button on:click=add_option>
           "Add another option to selector"
         </button>
         <button on:click=print>
           "Print"
         </button>
        </p>
      </div>
    }
}