Skip to content

Commit 7a55441

Browse files
rjmacRobert Macomber
andauthored
Add "replace route" call as a companion to "push route" (#2023)
* 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>
1 parent 0e52b88 commit 7a55441

6 files changed

Lines changed: 96 additions & 15 deletions

File tree

packages/yew-router/Cargo.toml

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,9 @@ categories = ["gui", "web-programming"]
1010
description = "A router implementation for the Yew framework"
1111
repository = "https://github.com/yewstack/yew"
1212

13+
[features]
14+
wasm_test = []
15+
1316
[dependencies]
1417
yew = { path = "../yew", default-features= false }
1518
yew-router-macro = { path = "../yew-router-macro" }

packages/yew-router/Makefile.toml

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
[tasks.test]
2+
extend = "core::wasm-pack-base"
3+
command = "wasm-pack"
4+
args = [
5+
"test",
6+
"@@split(YEW_TEST_FLAGS, )",
7+
"--",
8+
"--features",
9+
"${YEW_TEST_FEATURES}",
10+
]

packages/yew-router/src/service.rs

Lines changed: 45 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -6,18 +6,49 @@ use wasm_bindgen::JsValue;
66
use web_sys::Event;
77
use yew::Callback;
88

9-
/// Navigate to a specific route.
9+
/// Navigate to a specific route, pushing the new route onto the
10+
/// user's history stack.
1011
pub fn push_route(route: impl Routable) {
11-
push_impl(route.to_path())
12+
update_route_impl(route.to_path(), true)
1213
}
1314

14-
/// Navigate to a specific route with query parameters.
15+
/// Navigate to a specific route, replacing the current route on the
16+
/// user's history stack.
17+
pub fn replace_route(route: impl Routable) {
18+
update_route_impl(route.to_path(), false)
19+
}
20+
21+
/// Navigate to a specific route with query parameters, pushing the
22+
/// new route onto the user's history stack.
1523
///
1624
/// This should be used in cases where [`Link`](crate::prelude::Link) is insufficient.
1725
pub fn push_route_with_query<S>(
1826
route: impl Routable,
1927
query: S,
2028
) -> Result<(), serde_urlencoded::ser::Error>
29+
where
30+
S: Serialize,
31+
{
32+
update_route_with_query_impl(route, query, true)
33+
}
34+
35+
/// Navigate to a specific route with query parameters, replacing the
36+
/// current route on the user's history stack.
37+
pub fn replace_route_with_query<S>(
38+
route: impl Routable,
39+
query: S,
40+
) -> Result<(), serde_urlencoded::ser::Error>
41+
where
42+
S: Serialize,
43+
{
44+
update_route_with_query_impl(route, query, false)
45+
}
46+
47+
fn update_route_with_query_impl<S>(
48+
route: impl Routable,
49+
query: S,
50+
push: bool,
51+
) -> Result<(), serde_urlencoded::ser::Error>
2152
where
2253
S: Serialize,
2354
{
@@ -27,12 +58,12 @@ where
2758
url.push_str(&format!("?{}", query));
2859
}
2960

30-
push_impl(url);
61+
update_route_impl(url, push);
3162

3263
Ok(())
3364
}
3465

35-
fn push_impl(url: String) {
66+
fn update_route_impl(url: String, push: bool) {
3667
let history = yew::utils::window().history().expect("no history");
3768
let base = base_url();
3869
let path = match base {
@@ -47,9 +78,15 @@ fn push_impl(url: String) {
4778
None => url,
4879
};
4980

50-
history
51-
.push_state_with_url(&JsValue::NULL, "", Some(&path))
52-
.expect("push history");
81+
if push {
82+
history
83+
.push_state_with_url(&JsValue::NULL, "", Some(&path))
84+
.expect("push history");
85+
} else {
86+
history
87+
.replace_state_with_url(&JsValue::NULL, "", Some(&path))
88+
.expect("replace history");
89+
}
5390
let event = Event::new("popstate").unwrap();
5491
yew::utils::window()
5592
.dispatch_event(&event)

packages/yew-router/tests/router.rs

Lines changed: 28 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ fn no(props: &NoProps) -> Html {
4444
#[function_component(Comp)]
4545
fn component() -> Html {
4646
let switch = Router::render(|routes| {
47-
let onclick = Callback::from(|_| {
48-
yew_router::push_route_with_query(
47+
let replace_route = Callback::from(|_| {
48+
yew_router::replace_route_with_query(
4949
Routes::No { id: 2 },
5050
Query {
5151
foo: "bar".to_string(),
@@ -54,14 +54,29 @@ fn component() -> Html {
5454
.unwrap();
5555
});
5656

57+
let push_route = Callback::from(|_| {
58+
yew_router::push_route_with_query(
59+
Routes::No { id: 3 },
60+
Query {
61+
foo: "baz".to_string(),
62+
},
63+
)
64+
.unwrap();
65+
});
66+
5767
match routes {
5868
Routes::Home => html! {
5969
<>
6070
<div id="result">{"Home"}</div>
61-
<a {onclick}>{"click me"}</a>
71+
<a onclick={replace_route}>{"replace a route"}</a>
72+
</>
73+
},
74+
Routes::No { id } => html! {
75+
<>
76+
<No id={*id} />
77+
<a onclick={push_route}>{"push a route"}</a>
6278
</>
6379
},
64-
Routes::No { id } => html! { <No id={*id} /> },
6580
Routes::NotFound => html! { <div id="result">{"404"}</div> },
6681
}
6782
});
@@ -86,7 +101,15 @@ fn router_works() {
86101

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

89-
click("a");
104+
let initial_length = history_length();
105+
106+
click("a"); // replacing the current route
90107
assert_eq!("2", obtain_result_by_id("result-params"));
91108
assert_eq!("bar", obtain_result_by_id("result-query"));
109+
assert_eq!(initial_length, history_length());
110+
111+
click("a"); // pushing a new route
112+
assert_eq!("3", obtain_result_by_id("result-params"));
113+
assert_eq!("baz", obtain_result_by_id("result-query"));
114+
assert_eq!(initial_length + 1, history_length());
92115
}

packages/yew-router/tests/utils.rs

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,3 +16,11 @@ pub fn click(selector: &str) {
1616
.unwrap()
1717
.click();
1818
}
19+
20+
pub fn history_length() -> u32 {
21+
yew::utils::window()
22+
.history()
23+
.expect("No history found")
24+
.length()
25+
.expect("No history length found")
26+
}

website/docs/concepts/router.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -80,13 +80,13 @@ fn switch(route: &Route) -> Html {
8080

8181
### Navigation
8282

83-
To navigate between pages, use either a `Link` component (which renders a `<a>` element) or the `yew_router::push_route` function.
83+
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.
8484

8585
### Query Parameters
8686

8787
#### Specifying query parameters when navigating
8888

89-
In order to specify query parameters when navigating to a new route, use `yew_router::push_route_with_query` function.
89+
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.
9090
It uses `serde` to serialize the parameters into query string for the URL so any type that implements `Serialize` can be passed.
9191
In its simplest form this is just a `HashMap` containing string pairs.
9292

0 commit comments

Comments
 (0)