Collection of minor changes (#63)

* various fixes to forms page

* add docs to `with!` macros

* show full path of `SubmitEvent`
This commit is contained in:
blorbb
2024-02-13 12:25:58 +11:00
committed by GitHub
parent 6d9d138a22
commit 5d23a7e35a
2 changed files with 57 additions and 47 deletions

View File

@@ -91,7 +91,7 @@ Instead, you can use the `with!` macro to get references to all the signals at t
let name = move || with!(|first, middle, last| format!("{first} {middle} {last}")); let name = move || with!(|first, middle, last| format!("{first} {middle} {last}"));
``` ```
This expands to the same thing as above. Take a look at the `with!` docs for more info, and the corresponding macros `update!`, `with_value!` and `update_value!`. This expands to the same thing as above. Take a look at the [`with!`](https://docs.rs/leptos/latest/leptos/macro.with.html) docs for more info, and the corresponding macros [`update!`](https://docs.rs/leptos/latest/leptos/macro.update.html), [`with_value!`](https://docs.rs/leptos/latest/leptos/macro.with_value.html) and [`update_value!`](https://docs.rs/leptos/latest/leptos/macro.update_value.html).
## Making signals depend on each other ## Making signals depend on each other

View File

@@ -75,50 +75,18 @@ view! {
In an "uncontrolled input," the browser controls the state of the input element. In an "uncontrolled input," the browser controls the state of the input element.
Rather than continuously updating a signal to hold its value, we use a Rather than continuously updating a signal to hold its value, we use a
[`NodeRef`](https://docs.rs/leptos/latest/leptos/struct.NodeRef.html) to access [`NodeRef`](https://docs.rs/leptos/latest/leptos/struct.NodeRef.html) to access
the input once when we want to get its value. the input when we want to get its value.
In this example, we only notify the framework when the `<form>` fires a `submit` In this example, we only notify the framework when the `<form>` fires a `submit` event.
event. Note the use of the [`leptos::html`](https://docs.rs/leptos/latest/leptos/html/index.html#) module, which provides a bunch of types for every HTML element.
```rust ```rust
let (name, set_name) = create_signal("Uncontrolled".to_string()); let (name, set_name) = create_signal("Uncontrolled".to_string());
let input_element: NodeRef<Input> = create_node_ref(); let input_element: NodeRef<html::Input> = create_node_ref();
```
`NodeRef` is a kind of reactive smart pointer: we can use it to access the
underlying DOM node. Its value will be set when the element is rendered.
```rust
let on_submit = move |ev: SubmitEvent| {
// stop the page from reloading!
ev.prevent_default();
// here, we'll extract the value from the input
let value = input_element()
// event handlers can only fire after the view
// is mounted to the DOM, so the `NodeRef` will be `Some`
.expect("<input> to exist")
// `NodeRef` implements `Deref` for the DOM element type
// this means we can call`HtmlInputElement::value()`
// to get the current value of the input
.value();
set_name(value);
};
```
Our `on_submit` handler will access the inputs value and use it to call `set_name`.
To access the DOM node stored in the `NodeRef`, we can simply call it as a function
(or using `.get()`). This will return `Option<web_sys::HtmlInputElement>`, but we
know it will already have been filled when we rendered the view, so its safe to
unwrap here.
We can then call `.value()` to get the value out of the input, because `NodeRef`
gives us access to a correctly-typed HTML element.
```rust
view! { view! {
<form on:submit=on_submit> <form on:submit=on_submit> // on_submit defined below
<input type="text" <input type="text"
value=name value=name
node_ref=input_element node_ref=input_element
@@ -134,9 +102,43 @@ The view should be pretty self-explanatory by now. Note two things:
1. Unlike in the controlled input example, we use `value` (not `prop:value`). 1. Unlike in the controlled input example, we use `value` (not `prop:value`).
This is because were just setting the initial value of the input, and letting This is because were just setting the initial value of the input, and letting
the browser control its state. (We could use `prop:value` instead.) the browser control its state. (We could use `prop:value` instead.)
2. We use `node_ref` to fill the `NodeRef`. (Older examples sometimes use `_ref`. 2. We use `node_ref=...` to fill the `NodeRef`. (Older examples sometimes use `_ref`.
They are the same thing, but `node_ref` has better rust-analyzer support.) They are the same thing, but `node_ref` has better rust-analyzer support.)
`NodeRef` is a kind of reactive smart pointer: we can use it to access the
underlying DOM node. Its value will be set when the element is rendered.
```rust
let on_submit = move |ev: leptos::ev::SubmitEvent| {
// stop the page from reloading!
ev.prevent_default();
// here, we'll extract the value from the input
let value = input_element()
// event handlers can only fire after the view
// is mounted to the DOM, so the `NodeRef` will be `Some`
.expect("<input> should be mounted")
// `leptos::HtmlElement<html::Input>` implements `Deref`
// to a `web_sys::HtmlInputElement`.
// this means we can call`HtmlInputElement::value()`
// to get the current value of the input
.value();
set_name(value);
};
```
Our `on_submit` handler will access the inputs value and use it to call `set_name`.
To access the DOM node stored in the `NodeRef`, we can simply call it as a function
(or using `.get()`). This will return `Option<leptos::HtmlElement<html::Input>>`, but we
know that the element has already been mounted (how else did you fire this event!), so
it's safe to unwrap here.
We can then call `.value()` to get the value out of the input, because `NodeRef`
gives us access to a correctly-typed HTML element.
Take a look at [`web_sys` and `HtmlElement`](../web_sys.md) to learn more about using a `leptos::HtmlElement`.
Also see the full CodeSandbox example at the end of this page.
## Special Cases: `<textarea>` and `<select>` ## Special Cases: `<textarea>` and `<select>`
Two form elements tend to cause some confusion, in different ways. Two form elements tend to cause some confusion, in different ways.
@@ -144,15 +146,15 @@ Two form elements tend to cause some confusion, in different ways.
### `<textarea>` ### `<textarea>`
Unlike `<input>`, the `<textarea>` element does not support a `value` attribute. Unlike `<input>`, the `<textarea>` element does not support a `value` attribute.
Instead, it receives its value as a plain text node in its HTML children, Instead, it receives its value as a plain text node in its HTML children.
In the current version of Leptos (in fact in Leptos 0.1-0.5), creating a dynamic child In the current version of Leptos (in fact in Leptos 0.1-0.6), creating a dynamic child
inserts a comment marker node. This can cause incorrect `<textarea>` rendering (and issues inserts a comment marker node. This can cause incorrect `<textarea>` rendering (and issues
during hydration) if you try to use it to show dynamic content. during hydration) if you try to use it to show dynamic content.
Instead, you can pass a non-reactive initial value as a child, and use `prop:value` to Instead, you can pass a non-reactive initial value as a child, and use `prop:value` to
set its current value. (`<textarea>` doesnt support the `value` attribute, but _does_ set its current value. (`<textarea>` doesnt support the `value` **attribute**, but _does_
support the `value` property...) support the `value` **property**...)
```rust ```rust
view! { view! {
@@ -160,20 +162,20 @@ view! {
prop:value=move || some_value.get() prop:value=move || some_value.get()
on:input=/* etc */ on:input=/* etc */
> >
/* untracked, plain-text initial value */ /* plain-text initial value, does not change if the signal changes */
{untrack(move || some_value.get())} {move || some_value.get_untracked()}
</textarea> </textarea>
} }
``` ```
### `<select>` ### `<select>`
The `<select>` element also does not have a `value` attribute, _or_ a `value` property. The `<select>` element also does not have a `value` attribute, _nor_ a `value` property.
Instead, its value is determined by the `selected` attribute of its `<option>` Instead, its value is determined by the `selected` attribute of its `<option>`
fields. Some frameworks obscure this with a `value` field on `<select>`; if you try this fields. Some frameworks obscure this with a `value` field on `<select>`; if you try this
in Leptos (or vanilla JavaScript) it wont work. in Leptos (or vanilla JavaScript) it wont work.
Instead, use the `selected` field: To use the `selected` field:
```rust ```rust
let (value, set_value) = create_signal("B".to_string()); let (value, set_value) = create_signal("B".to_string());
@@ -199,6 +201,7 @@ view! {
``` ```
That's somewhat repetitive, but can easily be refactored: That's somewhat repetitive, but can easily be refactored:
```rust ```rust
#[component] #[component]
pub fn App() -> impl IntoView { pub fn App() -> impl IntoView {
@@ -228,6 +231,13 @@ pub fn SelectOption(is: &'static str, value: ReadSignal<String>) -> impl IntoVie
} }
``` ```
> Tip: the single `value` attribute in the component is equivalent to `value=value`.
> This is only the case for _components_: in HTML elements, a single `value` attribute is equivalent to `value=true`.
> This is expected to be made consistent in the next major version of Leptos; see [this issue](https://github.com/leptos-rs/leptos/issues/2196)
> for more details.
**Controlled vs uncontrolled forms CodeSandbox:**
[Click to open CodeSandbox.](https://codesandbox.io/p/sandbox/5-forms-0-5-rf2t7c?file=%2Fsrc%2Fmain.rs%3A1%2C1) [Click to open CodeSandbox.](https://codesandbox.io/p/sandbox/5-forms-0-5-rf2t7c?file=%2Fsrc%2Fmain.rs%3A1%2C1)
<iframe src="https://codesandbox.io/p/sandbox/5-forms-0-5-rf2t7c?file=%2Fsrc%2Fmain.rs%3A1%2C1" width="100%" height="1000px" style="max-height: 100vh"></iframe> <iframe src="https://codesandbox.io/p/sandbox/5-forms-0-5-rf2t7c?file=%2Fsrc%2Fmain.rs%3A1%2C1" width="100%" height="1000px" style="max-height: 100vh"></iframe>