Skip to content

Commit

Permalink
Add "replace route" call as a companion to "push route" (#2023)
Browse files Browse the repository at this point in the history
* Add "replace route" call as a companion to "push route"

* Fix test

* Make the yew-router tests run

Co-authored-by: Robert Macomber <robertm@mox>
  • Loading branch information
rjmac and Robert Macomber authored Aug 29, 2021
1 parent 0e52b88 commit 7a55441
Show file tree
Hide file tree
Showing 6 changed files with 96 additions and 15 deletions.
3 changes: 3 additions & 0 deletions packages/yew-router/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,9 @@ categories = ["gui", "web-programming"]
description = "A router implementation for the Yew framework"
repository = "https://github.com/yewstack/yew"

[features]
wasm_test = []

[dependencies]
yew = { path = "../yew", default-features= false }
yew-router-macro = { path = "../yew-router-macro" }
Expand Down
10 changes: 10 additions & 0 deletions packages/yew-router/Makefile.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
[tasks.test]
extend = "core::wasm-pack-base"
command = "wasm-pack"
args = [
"test",
"@@split(YEW_TEST_FLAGS, )",
"--",
"--features",
"${YEW_TEST_FEATURES}",
]
53 changes: 45 additions & 8 deletions packages/yew-router/src/service.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,18 +6,49 @@ use wasm_bindgen::JsValue;
use web_sys::Event;
use yew::Callback;

/// Navigate to a specific route.
/// Navigate to a specific route, pushing the new route onto the
/// user's history stack.
pub fn push_route(route: impl Routable) {
push_impl(route.to_path())
update_route_impl(route.to_path(), true)
}

/// Navigate to a specific route with query parameters.
/// Navigate to a specific route, replacing the current route on the
/// user's history stack.
pub fn replace_route(route: impl Routable) {
update_route_impl(route.to_path(), false)
}

/// Navigate to a specific route with query parameters, pushing the
/// new route onto the user's history stack.
///
/// This should be used in cases where [`Link`](crate::prelude::Link) is insufficient.
pub fn push_route_with_query<S>(
route: impl Routable,
query: S,
) -> Result<(), serde_urlencoded::ser::Error>
where
S: Serialize,
{
update_route_with_query_impl(route, query, true)
}

/// Navigate to a specific route with query parameters, replacing the
/// current route on the user's history stack.
pub fn replace_route_with_query<S>(
route: impl Routable,
query: S,
) -> Result<(), serde_urlencoded::ser::Error>
where
S: Serialize,
{
update_route_with_query_impl(route, query, false)
}

fn update_route_with_query_impl<S>(
route: impl Routable,
query: S,
push: bool,
) -> Result<(), serde_urlencoded::ser::Error>
where
S: Serialize,
{
Expand All @@ -27,12 +58,12 @@ where
url.push_str(&format!("?{}", query));
}

push_impl(url);
update_route_impl(url, push);

Ok(())
}

fn push_impl(url: String) {
fn update_route_impl(url: String, push: bool) {
let history = yew::utils::window().history().expect("no history");
let base = base_url();
let path = match base {
Expand All @@ -47,9 +78,15 @@ fn push_impl(url: String) {
None => url,
};

history
.push_state_with_url(&JsValue::NULL, "", Some(&path))
.expect("push history");
if push {
history
.push_state_with_url(&JsValue::NULL, "", Some(&path))
.expect("push history");
} else {
history
.replace_state_with_url(&JsValue::NULL, "", Some(&path))
.expect("replace history");
}
let event = Event::new("popstate").unwrap();
yew::utils::window()
.dispatch_event(&event)
Expand Down
33 changes: 28 additions & 5 deletions packages/yew-router/tests/router.rs
Original file line number Diff line number Diff line change
Expand Up @@ -44,8 +44,8 @@ fn no(props: &NoProps) -> Html {
#[function_component(Comp)]
fn component() -> Html {
let switch = Router::render(|routes| {
let onclick = Callback::from(|_| {
yew_router::push_route_with_query(
let replace_route = Callback::from(|_| {
yew_router::replace_route_with_query(
Routes::No { id: 2 },
Query {
foo: "bar".to_string(),
Expand All @@ -54,14 +54,29 @@ fn component() -> Html {
.unwrap();
});

let push_route = Callback::from(|_| {
yew_router::push_route_with_query(
Routes::No { id: 3 },
Query {
foo: "baz".to_string(),
},
)
.unwrap();
});

match routes {
Routes::Home => html! {
<>
<div id="result">{"Home"}</div>
<a {onclick}>{"click me"}</a>
<a onclick={replace_route}>{"replace a route"}</a>
</>
},
Routes::No { id } => html! {
<>
<No id={*id} />
<a onclick={push_route}>{"push a route"}</a>
</>
},
Routes::No { id } => html! { <No id={*id} /> },
Routes::NotFound => html! { <div id="result">{"404"}</div> },
}
});
Expand All @@ -86,7 +101,15 @@ fn router_works() {

assert_eq!("Home", obtain_result_by_id("result"));

click("a");
let initial_length = history_length();

click("a"); // replacing the current route
assert_eq!("2", obtain_result_by_id("result-params"));
assert_eq!("bar", obtain_result_by_id("result-query"));
assert_eq!(initial_length, history_length());

click("a"); // pushing a new route
assert_eq!("3", obtain_result_by_id("result-params"));
assert_eq!("baz", obtain_result_by_id("result-query"));
assert_eq!(initial_length + 1, history_length());
}
8 changes: 8 additions & 0 deletions packages/yew-router/tests/utils.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,3 +16,11 @@ pub fn click(selector: &str) {
.unwrap()
.click();
}

pub fn history_length() -> u32 {
yew::utils::window()
.history()
.expect("No history found")
.length()
.expect("No history length found")
}
4 changes: 2 additions & 2 deletions website/docs/concepts/router.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,13 @@ fn switch(route: &Route) -> Html {

### Navigation

To navigate between pages, use either a `Link` component (which renders a `<a>` element) or the `yew_router::push_route` function.
To navigate between pages, use either a `Link` component (which renders a `<a>` element), the `yew_router::push_route` function, or the `yew_router::replace_route` function, which replaces the current page in the user's browser history instead of pushing a new one onto the stack.

### Query Parameters

#### Specifying query parameters when navigating

In order to specify query parameters when navigating to a new route, use `yew_router::push_route_with_query` function.
In order to specify query parameters when navigating to a new route, use either `yew_router::push_route_with_query` or the `yew_router::replace_route_with_query` functions.
It uses `serde` to serialize the parameters into query string for the URL so any type that implements `Serialize` can be passed.
In its simplest form this is just a `HashMap` containing string pairs.

Expand Down

0 comments on commit 7a55441

Please sign in to comment.