Binding inputs and forms
Two-way binding
Keep the content of the field in a Value, pass it as value attribute, and update it in the on_input handler, which gets the new content on every keystroke:
use vertigo::{bind, dom, Value}; let name = Value::new("John".to_string()); let on_input = bind!(name, |new_value: String| name.set(new_value)); let clear = bind!(name, |_| name.set(String::new())); let greeting = name.map(|name| format!("Hello, {name}!")); dom! { <div> <input value={&name} {on_input} /> <button on_click={clear}>"Clear"</button> <p>{greeting}</p> </div> }
The binding works both ways: for <input> and <textarea> vertigo sets not only the value attribute but also the value of the element itself, so setting the Value from code (like the "Clear" button does) updates the field even after the user typed something in it.
on_change
Use on_change if you're interested only in the final value. For text fields it's called when the user commits the value (presses Enter or leaves the field). It also works for <select>, giving the value of the chosen option:
use vertigo::{bind, dom, Value}; let city = Value::new(String::new()); let size = Value::new("M".to_string()); // Called when the user commits the value (Enter, leaving the field), // not on every keystroke like `on_input` let on_city_change = bind!(city, |new_value: String| city.set(new_value)); // For `<select>` it's called right after choosing an option let on_size_change = bind!(size, |new_value: String| size.set(new_value)); dom! { <div> <input value={&city} on_change={on_city_change} /> <select on_change={on_size_change}> <option value="S">"Small"</option> <option value="M" selected="selected">"Medium"</option> <option value="L">"Large"</option> </select> <p>"City: " {city} ", size: " {size}</p> </div> }
Submitting a form
on_submit is called when the form gets submitted, by a submit button or by pressing Enter in a text input. The default action of the browser is prevented, so the page doesn't reload:
use vertigo::{bind, dom, transaction, Value}; let draft = Value::new(String::new()); let messages = Value::new(Vec::<String>::new()); let on_input = bind!(draft, |text: String| draft.set(text)); let on_submit = bind!(draft, messages, || { let text = transaction(|ctx| draft.get(ctx)); if !text.trim().is_empty() { messages.change(|messages| messages.push(text)); draft.set(String::new()); } }); let list = messages.render_value(|messages| { dom! { <ul> {..messages.into_iter().map(|message| dom! { <li>{message}</li> })} </ul> } }); dom! { <div> <form {on_submit}> <textarea value={&draft} {on_input} /> <button type="submit">"Send"</button> </form> {list} </div> }
To read a Value outside of a reactive context, like in the handler above, use transaction.