Skip to content

Commit

Permalink
Two-way data binding (#2977)
Browse files Browse the repository at this point in the history
* added two-way data binding to dom elements

* added two-way data binding to radio groups

* moved bind into reactive_graph mod

* chore: use `::leptos` absolute path in macro

* chore: move `Group` into the reactive module, as that's the only place it has meaning

* feat: use new `bind:` syntax

* chore: update for non-generic rendering

* chore: ignore this test

---------

Co-authored-by: Greg Johnston <greg.johnston@gmail.com>
  • Loading branch information
maccesch and gbj authored Oct 1, 2024
1 parent d10fec0 commit 013ec4a
Show file tree
Hide file tree
Showing 9 changed files with 570 additions and 37 deletions.
5 changes: 1 addition & 4 deletions examples/todomvc/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -319,10 +319,7 @@ pub fn Todo(todo: Todo) -> impl IntoView {
node_ref=todo_input
class="toggle"
type="checkbox"
prop:checked=move || todo.completed.get()
on:input:target=move |ev| {
todo.completed.set(ev.target().checked());
}
bind:checked=todo.completed
/>

<label on:dblclick=move |_| {
Expand Down
2 changes: 1 addition & 1 deletion leptos/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -174,7 +174,7 @@ pub mod prelude {
pub use server_fn::{self, ServerFnError};
pub use tachys::{
self,
reactive_graph::{node_ref::*, Suspend},
reactive_graph::{bind::BindAttribute, node_ref::*, Suspend},
view::template::ViewTemplate,
};
}
Expand Down
13 changes: 3 additions & 10 deletions leptos_macro/src/view/component_builder.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
use super::{fragment_to_tokens, TagType};
use crate::view::attribute_absolute;
use crate::view::{attribute_absolute, utils::filter_prefixed_attrs};
use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::{format_ident, quote, quote_spanned};
use rstml::node::{
Expand Down Expand Up @@ -105,20 +105,13 @@ pub(crate) fn component_to_tokens(
return None;
}
};

let inputs = &binding.inputs;
Some(quote! { #inputs })
})
.collect::<Vec<_>>();

let items_to_clone = attrs
.iter()
.filter_map(|attr| {
attr.key
.to_string()
.strip_prefix("clone:")
.map(|ident| format_ident!("{ident}", span = attr.key.span()))
})
.collect::<Vec<_>>();
let items_to_clone = filter_prefixed_attrs(attrs.iter(), "clone:");

// include all attribute that are either
// 1) blocks ({..attrs} or {attrs}),
Expand Down
25 changes: 23 additions & 2 deletions leptos_macro/src/view/mod.rs
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
mod component_builder;
mod slot_helper;
mod utils;

use self::{
component_builder::component_to_tokens,
slot_helper::{get_slot, slot_to_tokens},
};
use convert_case::{Case::Snake, Casing};
use convert_case::{
Case::{Snake, UpperCamel},
Casing,
};
use leptos_hot_reload::parsing::{is_component_node, value_to_string};
use proc_macro2::{Ident, Span, TokenStream, TokenTree};
use proc_macro_error2::abort;
use quote::{quote, quote_spanned, ToTokens};
use quote::{format_ident, quote, quote_spanned, ToTokens};
use rstml::node::{
CustomNode, KVAttributeValue, KeyedAttribute, Node, NodeAttribute,
NodeBlock, NodeElement, NodeName, NodeNameFragment,
Expand Down Expand Up @@ -874,6 +879,8 @@ fn attribute_to_tokens(
directive_call_from_attribute_node(node, name)
} else if let Some(name) = name.strip_prefix("on:") {
event_to_tokens(name, node)
} else if let Some(name) = name.strip_prefix("bind:") {
two_way_binding_to_tokens(name, node)
} else if let Some(name) = name.strip_prefix("class:") {
let class = match &node.key {
NodeName::Punctuated(parts) => &parts[0],
Expand Down Expand Up @@ -1057,6 +1064,20 @@ pub(crate) fn attribute_absolute(
}
}

pub(crate) fn two_way_binding_to_tokens(
name: &str,
node: &KeyedAttribute,
) -> TokenStream {
let value = attribute_value(node);

let ident =
format_ident!("{}", name.to_case(UpperCamel), span = node.key.span());

quote! {
.bind(::leptos::attr::#ident, #value)
}
}

pub(crate) fn event_to_tokens(
name: &str,
node: &KeyedAttribute,
Expand Down
24 changes: 4 additions & 20 deletions leptos_macro/src/view/slot_helper.rs
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
use super::{convert_to_snake_case, ident_from_tag_name};
use crate::view::{fragment_to_tokens, TagType};
use crate::view::{fragment_to_tokens, utils::filter_prefixed_attrs, TagType};
use proc_macro2::{Ident, TokenStream, TokenTree};
use quote::{format_ident, quote, quote_spanned};
use quote::{quote, quote_spanned};
use rstml::node::{CustomNode, KeyedAttribute, NodeAttribute, NodeElement};
use std::collections::HashMap;
use syn::spanned::Spanned;
Expand Down Expand Up @@ -70,25 +70,9 @@ pub(crate) fn slot_to_tokens(
}
});

let items_to_bind = attrs
.iter()
.filter_map(|attr| {
attr.key
.to_string()
.strip_prefix("let:")
.map(|ident| format_ident!("{ident}", span = attr.key.span()))
})
.collect::<Vec<_>>();
let items_to_bind = filter_prefixed_attrs(attrs.iter(), "let:");

let items_to_clone = attrs
.iter()
.filter_map(|attr| {
attr.key
.to_string()
.strip_prefix("clone:")
.map(|ident| format_ident!("{ident}", span = attr.key.span()))
})
.collect::<Vec<_>>();
let items_to_clone = filter_prefixed_attrs(attrs.iter(), "clone:");

let dyn_attrs = attrs
.iter()
Expand Down
19 changes: 19 additions & 0 deletions leptos_macro/src/view/utils.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
use proc_macro2::Ident;
use quote::format_ident;
use rstml::node::KeyedAttribute;
use syn::spanned::Spanned;

pub fn filter_prefixed_attrs<'a, A>(attrs: A, prefix: &str) -> Vec<Ident>
where
A: IntoIterator<Item = &'a KeyedAttribute> + Clone,
{
attrs
.into_iter()
.filter_map(|attr| {
attr.key
.to_string()
.strip_prefix(prefix)
.map(|ident| format_ident!("{ident}", span = attr.key.span()))
})
.collect()
}
1 change: 1 addition & 0 deletions tachys/src/html/attribute/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ pub mod custom;
pub mod global;
mod key;
mod value;

use crate::view::{Position, ToTemplate};
pub use key::*;
use std::{fmt::Debug, future::Future};
Expand Down
Loading

0 comments on commit 013ec4a

Please sign in to comment.