Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Collection of minor changes #63

Merged
merged 3 commits into from
Feb 13, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/reactivity/working_with_signals.md
Original file line number Diff line number Diff line change
Expand Up @@ -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}"));
```

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

Expand Down
86 changes: 48 additions & 38 deletions src/view/05_forms.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,31 +75,51 @@ view! {
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
[`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`
event.
In this example, we only notify the framework when the `<form>` fires a `submit` 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
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();

view! {
<form on:submit=on_submit> // on_submit defined below
<input type="text"
value=name
node_ref=input_element
/>
<input type="submit" value="Submit"/>
</form>
<p>"Name is: " {name}</p>
}
```

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`).
This is because we’re just setting the initial value of the input, and letting
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`.
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: SubmitEvent| {
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> to exist")
// `NodeRef` implements `Deref` for the DOM element type
.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();
Expand All @@ -109,33 +129,15 @@ let on_submit = move |ev: SubmitEvent| {

Our `on_submit` handler will access the input’s 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 it’s safe to
unwrap here.
(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.

```rust
view! {
<form on:submit=on_submit>
<input type="text"
value=name
node_ref=input_element
/>
<input type="submit" value="Submit"/>
</form>
<p>"Name is: " {name}</p>
}
```

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`).
This is because we’re just setting the initial value of the input, and letting
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`.
They are the same thing, but `node_ref` has better rust-analyzer support.)
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>`

Expand All @@ -144,36 +146,36 @@ Two form elements tend to cause some confusion, in different ways.
### `<textarea>`

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
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Updated this version, I think it probably hasn't changed in 0.6 but I haven't checked.

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.

Instead, you can pass a non-reactive initial value as a child, and use `prop:value` to
set its current value. (`<textarea>` doesn’t support the `value` attribute, but _does_
support the `value` property...)
set its current value. (`<textarea>` doesn’t support the `value` **attribute**, but _does_
support the `value` **property**...)

```rust
view! {
<textarea
prop:value=move || some_value.get()
on:input=/* etc */
>
/* untracked, plain-text initial value */
{untrack(move || some_value.get())}
/* plain-text initial value, does not change if the signal changes */
{move || some_value.get_untracked()}
</textarea>
}
```

### `<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>`
fields. Some frameworks obscure this with a `value` field on `<select>`; if you try this
in Leptos (or vanilla JavaScript) it won’t work.

Instead, use the `selected` field:
To use the `selected` field:

```rust
let (value, set_value) = create_signal("B".to_string());
Expand All @@ -199,6 +201,7 @@ view! {
```

That's somewhat repetitive, but can easily be refactored:

```rust
#[component]
pub fn App() -> impl IntoView {
Expand Down Expand Up @@ -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)

<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>
Expand Down