mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 06:44:17 +00:00
fix: provide parent params to children (closes #2719)
This commit is contained in:
parent
7ed4d08dab
commit
0222182286
4 changed files with 70 additions and 25 deletions
|
@ -9,7 +9,7 @@ use any_spawner::Executor;
|
||||||
use either_of::{Either, EitherOf3};
|
use either_of::{Either, EitherOf3};
|
||||||
use futures::FutureExt;
|
use futures::FutureExt;
|
||||||
use reactive_graph::{
|
use reactive_graph::{
|
||||||
computed::ScopedFuture,
|
computed::{ArcMemo, ScopedFuture},
|
||||||
owner::{provide_context, Owner},
|
owner::{provide_context, Owner},
|
||||||
signal::ArcRwSignal,
|
signal::ArcRwSignal,
|
||||||
traits::{ReadUntracked, Set},
|
traits::{ReadUntracked, Set},
|
||||||
|
@ -202,6 +202,7 @@ where
|
||||||
.map(|n| n.to_params().into_iter().collect())
|
.map(|n| n.to_params().into_iter().collect())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
let params_memo = ArcMemo::from(params.clone());
|
||||||
|
|
||||||
match new_match {
|
match new_match {
|
||||||
None => Rc::new(RefCell::new(FlatRoutesViewState {
|
None => Rc::new(RefCell::new(FlatRoutesViewState {
|
||||||
|
@ -224,10 +225,9 @@ where
|
||||||
|
|
||||||
let mut view = Box::pin(owner.with(|| {
|
let mut view = Box::pin(owner.with(|| {
|
||||||
ScopedFuture::new({
|
ScopedFuture::new({
|
||||||
let params = params.clone();
|
|
||||||
let url = url.clone();
|
let url = url.clone();
|
||||||
async move {
|
async move {
|
||||||
provide_context(params);
|
provide_context(params_memo);
|
||||||
provide_context(url);
|
provide_context(url);
|
||||||
view.choose().await
|
view.choose().await
|
||||||
}
|
}
|
||||||
|
@ -322,6 +322,7 @@ where
|
||||||
let owner = outer_owner.child();
|
let owner = outer_owner.child();
|
||||||
let url = ArcRwSignal::new(url_snapshot.to_owned());
|
let url = ArcRwSignal::new(url_snapshot.to_owned());
|
||||||
let params = ArcRwSignal::new(matched_params);
|
let params = ArcRwSignal::new(matched_params);
|
||||||
|
let params_memo = ArcMemo::from(params.clone());
|
||||||
let old_owner = mem::replace(&mut initial_state.owner, owner.clone());
|
let old_owner = mem::replace(&mut initial_state.owner, owner.clone());
|
||||||
let old_url = mem::replace(&mut initial_state.url, url.clone());
|
let old_url = mem::replace(&mut initial_state.url, url.clone());
|
||||||
let old_params =
|
let old_params =
|
||||||
|
@ -336,7 +337,7 @@ where
|
||||||
None => {
|
None => {
|
||||||
owner.with(|| {
|
owner.with(|| {
|
||||||
provide_context(url);
|
provide_context(url);
|
||||||
provide_context(params);
|
provide_context(params_memo);
|
||||||
EitherOf3::B(fallback())
|
EitherOf3::B(fallback())
|
||||||
.rebuild(&mut state.borrow_mut().view)
|
.rebuild(&mut state.borrow_mut().view)
|
||||||
});
|
});
|
||||||
|
@ -358,7 +359,7 @@ where
|
||||||
let state = Rc::clone(state);
|
let state = Rc::clone(state);
|
||||||
async move {
|
async move {
|
||||||
provide_context(url);
|
provide_context(url);
|
||||||
provide_context(params);
|
provide_context(params_memo);
|
||||||
let view =
|
let view =
|
||||||
if let Some(set_is_routing) = set_is_routing {
|
if let Some(set_is_routing) = set_is_routing {
|
||||||
set_is_routing.set(true);
|
set_is_routing.set(true);
|
||||||
|
@ -439,6 +440,7 @@ where
|
||||||
.map(|n| n.to_params().into_iter().collect::<ParamsMap>())
|
.map(|n| n.to_params().into_iter().collect::<ParamsMap>())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
let params_memo = ArcMemo::from(params.clone());
|
||||||
let view = match new_match {
|
let view = match new_match {
|
||||||
None => Either::Left((self.fallback)()),
|
None => Either::Left((self.fallback)()),
|
||||||
Some(matched) => {
|
Some(matched) => {
|
||||||
|
@ -447,7 +449,7 @@ where
|
||||||
.with(|| {
|
.with(|| {
|
||||||
ScopedFuture::new(async move {
|
ScopedFuture::new(async move {
|
||||||
provide_context(url);
|
provide_context(url);
|
||||||
provide_context(params);
|
provide_context(params_memo);
|
||||||
view.choose().await
|
view.choose().await
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
@ -594,6 +596,7 @@ where
|
||||||
.map(|n| n.to_params().into_iter().collect())
|
.map(|n| n.to_params().into_iter().collect())
|
||||||
.unwrap_or_default(),
|
.unwrap_or_default(),
|
||||||
);
|
);
|
||||||
|
let params_memo = ArcMemo::from(params.clone());
|
||||||
|
|
||||||
match new_match {
|
match new_match {
|
||||||
None => Rc::new(RefCell::new(FlatRoutesViewState {
|
None => Rc::new(RefCell::new(FlatRoutesViewState {
|
||||||
|
@ -617,10 +620,9 @@ where
|
||||||
|
|
||||||
let mut view = Box::pin(owner.with(|| {
|
let mut view = Box::pin(owner.with(|| {
|
||||||
ScopedFuture::new({
|
ScopedFuture::new({
|
||||||
let params = params.clone();
|
|
||||||
let url = url.clone();
|
let url = url.clone();
|
||||||
async move {
|
async move {
|
||||||
provide_context(params);
|
provide_context(params_memo);
|
||||||
provide_context(url);
|
provide_context(url);
|
||||||
view.choose().await
|
view.choose().await
|
||||||
}
|
}
|
||||||
|
|
|
@ -156,7 +156,7 @@ pub fn use_location() -> Location {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
fn use_params_raw() -> ArcRwSignal<ParamsMap> {
|
fn use_params_raw() -> ArcMemo<ParamsMap> {
|
||||||
use_context().expect(
|
use_context().expect(
|
||||||
"Tried to access params outside the context of a matched <Route>.",
|
"Tried to access params outside the context of a matched <Route>.",
|
||||||
)
|
)
|
||||||
|
@ -165,9 +165,7 @@ fn use_params_raw() -> ArcRwSignal<ParamsMap> {
|
||||||
/// Returns a raw key-value map of route params.
|
/// Returns a raw key-value map of route params.
|
||||||
#[track_caller]
|
#[track_caller]
|
||||||
pub fn use_params_map() -> Memo<ParamsMap> {
|
pub fn use_params_map() -> Memo<ParamsMap> {
|
||||||
// TODO this can be optimized in future to map over the signal, rather than cloning
|
use_params_raw().into()
|
||||||
let params = use_params_raw();
|
|
||||||
Memo::new(move |_| params.get())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Returns the current route params, parsed into the given type, or an error.
|
/// Returns the current route params, parsed into the given type, or an error.
|
||||||
|
|
|
@ -569,15 +569,27 @@ where
|
||||||
// the matched signal will also be updated on every match
|
// the matched signal will also be updated on every match
|
||||||
// it's used for relative route resolution
|
// it's used for relative route resolution
|
||||||
let matched = ArcRwSignal::new(self.as_matched().to_string());
|
let matched = ArcRwSignal::new(self.as_matched().to_string());
|
||||||
let matched_including_parents = {
|
let (parent_params, parent_matches): (Vec<_>, Vec<_>) = outlets
|
||||||
let parents = outlets
|
|
||||||
.iter()
|
.iter()
|
||||||
.map(|route| route.matched.clone())
|
.map(|route| (route.params.clone(), route.matched.clone()))
|
||||||
.collect::<Vec<_>>();
|
.unzip();
|
||||||
|
let params_including_parents = {
|
||||||
|
let params = params.clone();
|
||||||
|
ArcMemo::new({
|
||||||
|
move |_| {
|
||||||
|
parent_params
|
||||||
|
.iter()
|
||||||
|
.flat_map(|params| params.get().into_iter())
|
||||||
|
.chain(params.get())
|
||||||
|
.collect::<ParamsMap>()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
let matched_including_parents = {
|
||||||
let matched = matched.clone();
|
let matched = matched.clone();
|
||||||
ArcMemo::new({
|
ArcMemo::new({
|
||||||
move |_| {
|
move |_| {
|
||||||
parents
|
parent_matches
|
||||||
.iter()
|
.iter()
|
||||||
.map(|matched| matched.get())
|
.map(|matched| matched.get())
|
||||||
.chain(iter::once(matched.get()))
|
.chain(iter::once(matched.get()))
|
||||||
|
@ -616,12 +628,11 @@ where
|
||||||
loaders.push(Box::pin(owner.with(|| {
|
loaders.push(Box::pin(owner.with(|| {
|
||||||
ScopedFuture::new({
|
ScopedFuture::new({
|
||||||
let owner = outlet.owner.clone();
|
let owner = outlet.owner.clone();
|
||||||
let params = outlet.params.clone();
|
|
||||||
let url = outlet.url.clone();
|
let url = outlet.url.clone();
|
||||||
let matched = Matched(matched_including_parents);
|
let matched = Matched(matched_including_parents);
|
||||||
let view_fn = Arc::clone(&outlet.view_fn);
|
let view_fn = Arc::clone(&outlet.view_fn);
|
||||||
async move {
|
async move {
|
||||||
provide_context(params);
|
provide_context(params_including_parents);
|
||||||
provide_context(url);
|
provide_context(url);
|
||||||
provide_context(matched);
|
provide_context(matched);
|
||||||
view.preload().await;
|
view.preload().await;
|
||||||
|
@ -667,11 +678,11 @@ where
|
||||||
outlets: &mut Vec<RouteContext<R>>,
|
outlets: &mut Vec<RouteContext<R>>,
|
||||||
parent: &Owner,
|
parent: &Owner,
|
||||||
) {
|
) {
|
||||||
let parent_matches = outlets
|
let (parent_params, parent_matches): (Vec<_>, Vec<_>) = outlets
|
||||||
.iter()
|
.iter()
|
||||||
.take(*items)
|
.take(*items)
|
||||||
.map(|route| route.matched.clone())
|
.map(|route| (route.params.clone(), route.matched.clone()))
|
||||||
.collect::<Vec<_>>();
|
.unzip();
|
||||||
let current = outlets.get_mut(*items);
|
let current = outlets.get_mut(*items);
|
||||||
match current {
|
match current {
|
||||||
// if there's nothing currently in the routes at this point, build from here
|
// if there's nothing currently in the routes at this point, build from here
|
||||||
|
@ -727,6 +738,18 @@ where
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
};
|
};
|
||||||
|
let params_including_parents = {
|
||||||
|
let params = current.params.clone();
|
||||||
|
ArcMemo::new({
|
||||||
|
move |_| {
|
||||||
|
parent_params
|
||||||
|
.iter()
|
||||||
|
.flat_map(|params| params.get().into_iter())
|
||||||
|
.chain(params.get())
|
||||||
|
.collect::<ParamsMap>()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
};
|
||||||
|
|
||||||
// assign a new owner, so that contexts and signals owned by the previous route
|
// assign a new owner, so that contexts and signals owned by the previous route
|
||||||
// in this outlet can be dropped
|
// in this outlet can be dropped
|
||||||
|
@ -742,11 +765,10 @@ where
|
||||||
let owner = owner.clone();
|
let owner = owner.clone();
|
||||||
let trigger = current.trigger.clone();
|
let trigger = current.trigger.clone();
|
||||||
let url = current.url.clone();
|
let url = current.url.clone();
|
||||||
let params = current.params.clone();
|
|
||||||
let matched = Matched(matched_including_parents);
|
let matched = Matched(matched_including_parents);
|
||||||
let view_fn = Arc::clone(¤t.view_fn);
|
let view_fn = Arc::clone(¤t.view_fn);
|
||||||
async move {
|
async move {
|
||||||
provide_context(params);
|
provide_context(params_including_parents);
|
||||||
provide_context(url);
|
provide_context(url);
|
||||||
provide_context(matched);
|
provide_context(matched);
|
||||||
view.preload().await;
|
view.preload().await;
|
||||||
|
|
|
@ -2,8 +2,10 @@ use crate::location::{unescape, Url};
|
||||||
use std::{borrow::Cow, mem, str::FromStr, sync::Arc};
|
use std::{borrow::Cow, mem, str::FromStr, sync::Arc};
|
||||||
use thiserror::Error;
|
use thiserror::Error;
|
||||||
|
|
||||||
|
type ParamsMapInner = Vec<(Cow<'static, str>, String)>;
|
||||||
|
|
||||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
|
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
|
||||||
pub struct ParamsMap(Vec<(Cow<'static, str>, String)>);
|
pub struct ParamsMap(ParamsMapInner);
|
||||||
|
|
||||||
impl ParamsMap {
|
impl ParamsMap {
|
||||||
/// Creates an empty map.
|
/// Creates an empty map.
|
||||||
|
@ -88,6 +90,27 @@ where
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
impl IntoIterator for ParamsMap {
|
||||||
|
type Item = (Cow<'static, str>, String);
|
||||||
|
type IntoIter = ParamsMapIter;
|
||||||
|
|
||||||
|
fn into_iter(self) -> Self::IntoIter {
|
||||||
|
ParamsMapIter(self.0.into_iter())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/// An iterator over the keys and values of a [`ParamsMap`].
|
||||||
|
#[derive(Debug)]
|
||||||
|
pub struct ParamsMapIter(<ParamsMapInner as IntoIterator>::IntoIter);
|
||||||
|
|
||||||
|
impl Iterator for ParamsMapIter {
|
||||||
|
type Item = (Cow<'static, str>, String);
|
||||||
|
|
||||||
|
fn next(&mut self) -> Option<Self::Item> {
|
||||||
|
self.0.next()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/// A simple method of deserializing key-value data (like route params or URL search)
|
/// A simple method of deserializing key-value data (like route params or URL search)
|
||||||
/// into a concrete data type. `Self` should typically be a struct in which
|
/// into a concrete data type. `Self` should typically be a struct in which
|
||||||
/// each field's type implements [`FromStr`].
|
/// each field's type implements [`FromStr`].
|
||||||
|
|
Loading…
Reference in a new issue