Implement hash fragments in the router (#2320)
* implement hash fragments in the router * clean up query and search example --------- Co-authored-by: Jonathan Kelley <jkelleyrtp@gmail.com>
This commit is contained in:
1 parent
dd8e69635b
commit
08da9a1253
17 files changed
+550
-190
No files matched your search
@@ -76,7 +76,6 @@ impl RouterContext {
|
||||
mark_dirty: Arc<dyn Fn(ScopeId) + Sync + Send>,
|
||||
) -> Self
|
||||
where
|
||||
R: Clone,
|
||||
<R as std::str::FromStr>::Err: std::fmt::Display,
|
||||
{
|
||||
let subscriber_update = mark_dirty.clone();
|
||||
|
||||
@@ -128,7 +128,8 @@ where
|
||||
fn route_from_location(&self) -> R {
|
||||
let location = self.window.location();
|
||||
let path = location.pathname().unwrap_or_else(|_| "/".into())
|
||||
+ &location.search().unwrap_or("".into());
|
||||
+ &location.search().unwrap_or("".into())
|
||||
+ &location.hash().unwrap_or("".into());
|
||||
let path = match self.prefix {
|
||||
None => path,
|
||||
Some(ref prefix) => {
|
||||
|
||||
@@ -36,7 +36,7 @@ pub trait FromQuery {
|
||||
|
||||
impl<T: for<'a> From<&'a str>> FromQuery for T {
|
||||
fn from_query(query: &str) -> Self {
|
||||
T::from(&*urlencoding::decode(query).expect("Failed to decode url encoding"))
|
||||
T::from(query)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -58,14 +58,7 @@ where
|
||||
type Err = <T as FromStr>::Err;
|
||||
|
||||
fn from_query_argument(argument: &str) -> Result<Self, Self::Err> {
|
||||
let result = match urlencoding::decode(argument) {
|
||||
Ok(argument) => T::from_str(&argument),
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to decode url encoding: {}", err);
|
||||
T::from_str(argument)
|
||||
}
|
||||
};
|
||||
match result {
|
||||
match T::from_str(argument) {
|
||||
Ok(result) => Ok(result),
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to parse query argument: {}", err);
|
||||
@@ -75,6 +68,82 @@ where
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can be created from an entire hash fragment.
|
||||
///
|
||||
/// This trait needs to be implemented if you want to turn a hash fragment into a struct.
|
||||
///
|
||||
/// # Example
|
||||
///
|
||||
/// ```rust
|
||||
/// use dioxus::prelude::*;
|
||||
///
|
||||
/// #[derive(Routable, Clone)]
|
||||
/// #[rustfmt::skip]
|
||||
/// enum Route {
|
||||
/// // State is stored in the url hash
|
||||
/// #[route("/#:url_hash")]
|
||||
/// Home {
|
||||
/// url_hash: State,
|
||||
/// },
|
||||
/// }
|
||||
///
|
||||
/// #[component]
|
||||
/// fn Home(url_hash: State) -> Element {
|
||||
/// todo!()
|
||||
/// }
|
||||
///
|
||||
///
|
||||
/// #[derive(Clone, PartialEq, Default)]
|
||||
/// struct State {
|
||||
/// count: usize,
|
||||
/// other_count: usize
|
||||
/// }
|
||||
///
|
||||
/// // The hash segment will be displayed as a string (this will be url encoded automatically)
|
||||
/// impl std::fmt::Display for State {
|
||||
/// fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
|
||||
/// write!(f, "{}-{}", self.count, self.other_count)
|
||||
/// }
|
||||
/// }
|
||||
///
|
||||
/// // We need to parse the hash fragment into a struct from the string (this will be url decoded automatically)
|
||||
/// impl FromHashFragment for State {
|
||||
/// fn from_hash_fragment(hash: &str) -> Self {
|
||||
/// let Some((first, second)) = hash.split_once('-') else {
|
||||
/// // URL fragment parsing shouldn't fail. You can return a default value if you want
|
||||
/// return Default::default();
|
||||
/// };
|
||||
///
|
||||
/// let first = first.parse().unwrap();
|
||||
/// let second = second.parse().unwrap();
|
||||
///
|
||||
/// State {
|
||||
/// count: first,
|
||||
/// other_count: second,
|
||||
/// }
|
||||
/// }
|
||||
/// }
|
||||
pub trait FromHashFragment {
|
||||
/// Create an instance of `Self` from a hash fragment.
|
||||
fn from_hash_fragment(hash: &str) -> Self;
|
||||
}
|
||||
|
||||
impl<T> FromHashFragment for T
|
||||
where
|
||||
T: FromStr + Default,
|
||||
T::Err: std::fmt::Display,
|
||||
{
|
||||
fn from_hash_fragment(hash: &str) -> Self {
|
||||
match T::from_str(hash) {
|
||||
Ok(value) => value,
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to parse hash fragment: {}", err);
|
||||
Default::default()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// Something that can be created from a route segment.
|
||||
pub trait FromRouteSegment: Sized {
|
||||
/// The error that can occur when parsing a route segment.
|
||||
@@ -91,13 +160,7 @@ where
|
||||
type Err = <T as FromStr>::Err;
|
||||
|
||||
fn from_route_segment(route: &str) -> Result<Self, Self::Err> {
|
||||
match urlencoding::decode(route) {
|
||||
Ok(segment) => T::from_str(&segment),
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to decode url encoding: {}", err);
|
||||
T::from_str(route)
|
||||
}
|
||||
}
|
||||
T::from_str(route)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -109,7 +172,7 @@ fn full_circle() {
|
||||
|
||||
/// Something that can be converted to route segments.
|
||||
pub trait ToRouteSegments {
|
||||
/// Display the route segments.
|
||||
/// Display the route segments. You must url encode the segments.
|
||||
fn display_route_segments(self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result;
|
||||
}
|
||||
|
||||
@@ -121,13 +184,8 @@ where
|
||||
for segment in self {
|
||||
write!(f, "/")?;
|
||||
let segment = segment.to_string();
|
||||
match urlencoding::decode(&segment) {
|
||||
Ok(segment) => write!(f, "{}", segment)?,
|
||||
Err(err) => {
|
||||
tracing::error!("Failed to decode url encoding: {}", err);
|
||||
write!(f, "{}", segment)?
|
||||
}
|
||||
}
|
||||
let encoded = urlencoding::encode(&segment);
|
||||
write!(f, "{}", encoded)?;
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
use crate::contexts::router::RoutingCallback;
|
||||
use crate::history::HistoryProvider;
|
||||
use crate::prelude::*;
|
||||
use crate::routable::Routable;
|
||||
use dioxus_lib::prelude::*;
|
||||
use std::sync::Arc;
|
||||
|
||||
|
||||
Reference in new issue
Block a user