smart-house-web: leptos эсперименты
This commit is contained in:
@@ -1,49 +1,114 @@
|
|||||||
#![allow(non_snake_case)]
|
#![allow(non_snake_case)]
|
||||||
use leptos::prelude::*;
|
|
||||||
|
use leptos::{ev::MouseEvent, prelude::*};
|
||||||
|
|
||||||
|
// This highlights four different ways that child components can communicate
|
||||||
|
// with their parent:
|
||||||
|
// 1) <ButtonA/>: passing a WriteSignal as one of the child component props,
|
||||||
|
// for the child component to write into and the parent to read
|
||||||
|
// 2) <ButtonB/>: passing a closure as one of the child component props, for
|
||||||
|
// the child component to call
|
||||||
|
// 3) <ButtonC/>: adding an `on:` event listener to a component
|
||||||
|
// 4) <ButtonD/>: providing a context that is used in the component (rather than prop drilling)
|
||||||
|
|
||||||
|
#[derive(Copy, Clone)]
|
||||||
|
struct SmallcapsContext(WriteSignal<bool>);
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
fn App() -> impl IntoView {
|
pub fn App() -> impl IntoView {
|
||||||
let (value, set_value) = signal(Ok(0));
|
// just some signals to toggle four classes on our <p>
|
||||||
|
let (red, set_red) = signal(false);
|
||||||
|
let (right, set_right) = signal(false);
|
||||||
|
let (italics, set_italics) = signal(false);
|
||||||
|
let (smallcaps, set_smallcaps) = signal(false);
|
||||||
|
|
||||||
|
// the newtype pattern isn't *necessary* here but is a good practice
|
||||||
|
// it avoids confusion with other possible future `WriteSignal<bool>` contexts
|
||||||
|
// and makes it easier to refer to it in ButtonD
|
||||||
|
provide_context(SmallcapsContext(set_smallcaps));
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<h1>"Error Handling"</h1>
|
<main>
|
||||||
<label>
|
<p
|
||||||
"Type a number (or something that's not a number!)"
|
// class: attributes take F: Fn() => bool, and these signals all implement Fn()
|
||||||
<input type="number" on:input:target=move |ev| {
|
class:red=red
|
||||||
// when input changes, try to parse a number from the input
|
class:right=right
|
||||||
set_value.set(ev.target().value().parse::<i32>())
|
class:italics=italics
|
||||||
}/>
|
class:smallcaps=smallcaps
|
||||||
// If an `Err(_) had been rendered inside the <ErrorBoundary/>,
|
|
||||||
// the fallback will be displayed. Otherwise, the children of the
|
|
||||||
// <ErrorBoundary/> will be displayed.
|
|
||||||
<ErrorBoundary
|
|
||||||
// the fallback receives a signal containing current errors
|
|
||||||
fallback=|errors| view! {
|
|
||||||
<div class="error">
|
|
||||||
<p>"Not a number! Errors: "</p>
|
|
||||||
// we can render a list of errors
|
|
||||||
// as strings, if we'd like
|
|
||||||
<ul>
|
|
||||||
{move || errors.get()
|
|
||||||
.into_iter()
|
|
||||||
.map(|(_, e)| view! { <li>{e.to_string()}</li>})
|
|
||||||
.collect::<Vec<_>>()
|
|
||||||
}
|
|
||||||
</ul>
|
|
||||||
</div>
|
|
||||||
}
|
|
||||||
>
|
>
|
||||||
<p>
|
"Lorem ipsum sit dolor amet."
|
||||||
"You entered "
|
</p>
|
||||||
// because `value` is `Result<i32, _>`,
|
|
||||||
// it will render the `i32` if it is `Ok`,
|
// Button A: pass the signal setter
|
||||||
// and render nothing and trigger the error boundary
|
<ButtonA setter=set_red/>
|
||||||
// if it is `Err`. It's a signal, so this will dynamically
|
|
||||||
// update when `value` changes
|
// Button B: pass a closure
|
||||||
<strong>{value}</strong>
|
<ButtonB on_click=move |_| set_right.update(|value| *value = !*value)/>
|
||||||
</p>
|
|
||||||
</ErrorBoundary>
|
// Button C: use a regular event listener
|
||||||
</label>
|
// setting an event listener on a component like this applies it
|
||||||
|
// to each of the top-level elements the component returns
|
||||||
|
<ButtonC on:click=move |_| set_italics.update(|value| *value = !*value)/>
|
||||||
|
|
||||||
|
// Button D gets its setter from context rather than props
|
||||||
|
<ButtonD/>
|
||||||
|
</main>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Button A receives a signal setter and updates the signal itself
|
||||||
|
#[component]
|
||||||
|
pub fn ButtonA(
|
||||||
|
/// Signal that will be toggled when the button is clicked.
|
||||||
|
setter: WriteSignal<bool>,
|
||||||
|
) -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<button
|
||||||
|
on:click=move |_| setter.update(|value| *value = !*value)
|
||||||
|
>
|
||||||
|
"Toggle Red"
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Button B receives a closure
|
||||||
|
#[component]
|
||||||
|
pub fn ButtonB(
|
||||||
|
/// Callback that will be invoked when the button is clicked.
|
||||||
|
on_click: impl FnMut(MouseEvent) + 'static,
|
||||||
|
) -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<button
|
||||||
|
on:click=on_click
|
||||||
|
>
|
||||||
|
"Toggle Right"
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Button C is a dummy: it renders a button but doesn't handle
|
||||||
|
/// its click. Instead, the parent component adds an event listener.
|
||||||
|
#[component]
|
||||||
|
pub fn ButtonC() -> impl IntoView {
|
||||||
|
view! {
|
||||||
|
<button>
|
||||||
|
"Toggle Italics"
|
||||||
|
</button>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Button D is very similar to Button A, but instead of passing the setter as a prop
|
||||||
|
/// we get it from the context
|
||||||
|
#[component]
|
||||||
|
pub fn ButtonD() -> impl IntoView {
|
||||||
|
let setter = use_context::<SmallcapsContext>().unwrap().0;
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<button
|
||||||
|
on:click=move |_| setter.update(|value| *value = !*value)
|
||||||
|
>
|
||||||
|
"Toggle Small Caps"
|
||||||
|
</button>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user