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 futures::FutureExt;
|
||||
use reactive_graph::{
|
||||
computed::ScopedFuture,
|
||||
computed::{ArcMemo, ScopedFuture},
|
||||
owner::{provide_context, Owner},
|
||||
signal::ArcRwSignal,
|
||||
traits::{ReadUntracked, Set},
|
||||
|
@ -202,6 +202,7 @@ where
|
|||
.map(|n| n.to_params().into_iter().collect())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
let params_memo = ArcMemo::from(params.clone());
|
||||
|
||||
match new_match {
|
||||
None => Rc::new(RefCell::new(FlatRoutesViewState {
|
||||
|
@ -224,10 +225,9 @@ where
|
|||
|
||||
let mut view = Box::pin(owner.with(|| {
|
||||
ScopedFuture::new({
|
||||
let params = params.clone();
|
||||
let url = url.clone();
|
||||
async move {
|
||||
provide_context(params);
|
||||
provide_context(params_memo);
|
||||
provide_context(url);
|
||||
view.choose().await
|
||||
}
|
||||
|
@ -322,6 +322,7 @@ where
|
|||
let owner = outer_owner.child();
|
||||
let url = ArcRwSignal::new(url_snapshot.to_owned());
|
||||
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_url = mem::replace(&mut initial_state.url, url.clone());
|
||||
let old_params =
|
||||
|
@ -336,7 +337,7 @@ where
|
|||
None => {
|
||||
owner.with(|| {
|
||||
provide_context(url);
|
||||
provide_context(params);
|
||||
provide_context(params_memo);
|
||||
EitherOf3::B(fallback())
|
||||
.rebuild(&mut state.borrow_mut().view)
|
||||
});
|
||||
|
@ -358,7 +359,7 @@ where
|
|||
let state = Rc::clone(state);
|
||||
async move {
|
||||
provide_context(url);
|
||||
provide_context(params);
|
||||
provide_context(params_memo);
|
||||
let view =
|
||||
if let Some(set_is_routing) = set_is_routing {
|
||||
set_is_routing.set(true);
|
||||
|
@ -439,6 +440,7 @@ where
|
|||
.map(|n| n.to_params().into_iter().collect::<ParamsMap>())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
let params_memo = ArcMemo::from(params.clone());
|
||||
let view = match new_match {
|
||||
None => Either::Left((self.fallback)()),
|
||||
Some(matched) => {
|
||||
|
@ -447,7 +449,7 @@ where
|
|||
.with(|| {
|
||||
ScopedFuture::new(async move {
|
||||
provide_context(url);
|
||||
provide_context(params);
|
||||
provide_context(params_memo);
|
||||
view.choose().await
|
||||
})
|
||||
})
|
||||
|
@ -594,6 +596,7 @@ where
|
|||
.map(|n| n.to_params().into_iter().collect())
|
||||
.unwrap_or_default(),
|
||||
);
|
||||
let params_memo = ArcMemo::from(params.clone());
|
||||
|
||||
match new_match {
|
||||
None => Rc::new(RefCell::new(FlatRoutesViewState {
|
||||
|
@ -617,10 +620,9 @@ where
|
|||
|
||||
let mut view = Box::pin(owner.with(|| {
|
||||
ScopedFuture::new({
|
||||
let params = params.clone();
|
||||
let url = url.clone();
|
||||
async move {
|
||||
provide_context(params);
|
||||
provide_context(params_memo);
|
||||
provide_context(url);
|
||||
view.choose().await
|
||||
}
|
||||
|
|
|
@ -156,7 +156,7 @@ pub fn use_location() -> Location {
|
|||
}
|
||||
|
||||
#[track_caller]
|
||||
fn use_params_raw() -> ArcRwSignal<ParamsMap> {
|
||||
fn use_params_raw() -> ArcMemo<ParamsMap> {
|
||||
use_context().expect(
|
||||
"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.
|
||||
#[track_caller]
|
||||
pub fn use_params_map() -> Memo<ParamsMap> {
|
||||
// TODO this can be optimized in future to map over the signal, rather than cloning
|
||||
let params = use_params_raw();
|
||||
Memo::new(move |_| params.get())
|
||||
use_params_raw().into()
|
||||
}
|
||||
|
||||
/// 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
|
||||
// it's used for relative route resolution
|
||||
let matched = ArcRwSignal::new(self.as_matched().to_string());
|
||||
let (parent_params, parent_matches): (Vec<_>, Vec<_>) = outlets
|
||||
.iter()
|
||||
.map(|route| (route.params.clone(), route.matched.clone()))
|
||||
.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 parents = outlets
|
||||
.iter()
|
||||
.map(|route| route.matched.clone())
|
||||
.collect::<Vec<_>>();
|
||||
let matched = matched.clone();
|
||||
ArcMemo::new({
|
||||
move |_| {
|
||||
parents
|
||||
parent_matches
|
||||
.iter()
|
||||
.map(|matched| matched.get())
|
||||
.chain(iter::once(matched.get()))
|
||||
|
@ -616,12 +628,11 @@ where
|
|||
loaders.push(Box::pin(owner.with(|| {
|
||||
ScopedFuture::new({
|
||||
let owner = outlet.owner.clone();
|
||||
let params = outlet.params.clone();
|
||||
let url = outlet.url.clone();
|
||||
let matched = Matched(matched_including_parents);
|
||||
let view_fn = Arc::clone(&outlet.view_fn);
|
||||
async move {
|
||||
provide_context(params);
|
||||
provide_context(params_including_parents);
|
||||
provide_context(url);
|
||||
provide_context(matched);
|
||||
view.preload().await;
|
||||
|
@ -667,11 +678,11 @@ where
|
|||
outlets: &mut Vec<RouteContext<R>>,
|
||||
parent: &Owner,
|
||||
) {
|
||||
let parent_matches = outlets
|
||||
let (parent_params, parent_matches): (Vec<_>, Vec<_>) = outlets
|
||||
.iter()
|
||||
.take(*items)
|
||||
.map(|route| route.matched.clone())
|
||||
.collect::<Vec<_>>();
|
||||
.map(|route| (route.params.clone(), route.matched.clone()))
|
||||
.unzip();
|
||||
let current = outlets.get_mut(*items);
|
||||
match current {
|
||||
// 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
|
||||
// in this outlet can be dropped
|
||||
|
@ -742,11 +765,10 @@ where
|
|||
let owner = owner.clone();
|
||||
let trigger = current.trigger.clone();
|
||||
let url = current.url.clone();
|
||||
let params = current.params.clone();
|
||||
let matched = Matched(matched_including_parents);
|
||||
let view_fn = Arc::clone(¤t.view_fn);
|
||||
async move {
|
||||
provide_context(params);
|
||||
provide_context(params_including_parents);
|
||||
provide_context(url);
|
||||
provide_context(matched);
|
||||
view.preload().await;
|
||||
|
|
|
@ -2,8 +2,10 @@ use crate::location::{unescape, Url};
|
|||
use std::{borrow::Cow, mem, str::FromStr, sync::Arc};
|
||||
use thiserror::Error;
|
||||
|
||||
type ParamsMapInner = Vec<(Cow<'static, str>, String)>;
|
||||
|
||||
#[derive(Debug, Default, Clone, PartialEq, Eq, Hash)]
|
||||
pub struct ParamsMap(Vec<(Cow<'static, str>, String)>);
|
||||
pub struct ParamsMap(ParamsMapInner);
|
||||
|
||||
impl ParamsMap {
|
||||
/// 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)
|
||||
/// into a concrete data type. `Self` should typically be a struct in which
|
||||
/// each field's type implements [`FromStr`].
|
||||
|
|
Loading…
Reference in a new issue