Expand description
A router for Yew, supporting nesting.
§Usage
The nested router makes use of Yew’s context features. It injects a routing context, tied to
a type implementing the target::Target trait. It then is possible to scope/translate between
levels.
§Targets
“Targets” are the route targets, things the page can point to. They must be an enum,
implementing the target::Target trait. This can easily be done using the Target derive:
#[derive(Clone, Debug, PartialEq, Eq, Target)]
pub enum AppRoute {
#[target(index)]
Index,
Foo,
Bar,
}This created a target enum with three paths (/, /foo, /bar).
§Main router
Each application needs a main entry point for the router (Router). This simply injects the
routing context, and provides the necessary information internally. All children of the
component will simply be rendered.
#[function_component(MyApp)]
pub fn my_app() -> Html {
html!(
<Router<AppRoute>>
<MyContent/>
</Router<AppRoute>>
)
}§Switching content
Having the route context available, allows to switch based on its state. This is done using the
Switch component, which searches (upwards) for a matching routing context (of the target
type).
#[function_component(MyContent)]
pub fn my_content() -> Html {
html!(
<Switch<AppRoute> render={|target|match target {
AppRoute::Index => html!(<Index/>),
AppRoute::Foo => html!(<Foo/>),
AppRoute::Bar => html!(<Bar/>),
}}/>
)
}The Switch component does not have any children, as its content is evaluated from the render
callback.
If no target matched, then none of the switches will match either. If is possible to define a default target on the router.
§Nesting
When nesting, first the structure must be declared. Let’s adapt the example from above:
#[derive(Clone, Debug, PartialEq, Eq, Target)]
pub enum AppRoute {
#[target(index)]
Index,
Foo(#[target(default)] Details),
Bar {
id: String,
#[target(nested, default)]
details: Details,
},
}
#[derive(Clone, Debug, PartialEq, Eq, Target)]
pub enum Details {
Overview,
Code,
Metrics,
}
impl Default for Details {
fn default() -> Self {
Self::Overview
}
}This changed the following:
- It added a nested layer to
Foo, which by default will use theDetails::Overviewtarget. - It added a nested layer to
Bar- Capturing a path variable into
id - Then using a nested layer to
Details, again using the default target.
- Capturing a path variable into
This will process the following routes
| Path | Target |
|---|---|
/ | AppRoute::Index |
/foo, /foo/overview | AppRoute::Foo({id}::Overview) |
/foo/code | AppRoute::Foo(Details::Code) |
/foo/metrics | AppRoute::Foo({id}::Metrics) |
/bar | no match |
/bar/{id}, /foo/{id}/overview | AppRoute::Bar {id: "id", details: Details::Overview} |
/foo/{id}/code | AppRoute::Bar {id: "id", details: Details::Code} |
/foo/{id}/metrics | AppRoute::Bar {id: "id", details: Details::Metrics} |
§Scoping/Translating
The main router will only insert an routing context for the AppRoutes context. Now we need to
translate down the next level. This is done using the Scope component, which translates
“from” -> “to”, or Parent -> Child.
#[function_component(Foo)]
pub fn foo() -> Html {
html! (
<Scope<AppRoute, Details> mapper={AppRoute::mapper_foo}>
<Switch<Details> render={|target|html!(/* ... */)}/>
</Scope<AppRoute, Details>>
)
}The AppRoute::mapper_foo function was automatically created by the Target derive. It
translates upwards and downwards between the two levels.
NOTE: Targets having additional information do not get a mapper automatically created, as
that information might not be known on the lower levels.
In these cases you will have to implement the mapper yourself.
An example is provided for the target Page::D in the examples folder.
For a more complete example on nesting, see the full example in the examples folder.
§Navigating
There is an out-of-the-box component named components::Link, which allows to navigate to a
target. It is also possible to achieve the same, using the routing context, which can be
acquired using prelude::use_router.
use yew_nested_router::components::Link;
#[function_component(MyContent)]
pub fn my_content() -> Html {
html!(
<>
<ul>
<li><Link<AppRoute> to={AppRoute::Index}>{"Index"}</Link<AppRoute>></li>
<li><Link<AppRoute> to={AppRoute::Foo(Details::Overview)}>{"Foo"}</Link<AppRoute>></li>
<li><Link<AppRoute> to={AppRoute::Bar{ id: "".into(), details: Details::Overview}}>{"Bar"}</Link<AppRoute>></li>
</ul>
<Switch<AppRoute> render={|target|match target {
AppRoute::Index => html!(<Index/>),
AppRoute::Foo(_) => html!(<Foo/>),
AppRoute::Bar{..} => html!(<Bar/>),
}}/>
</>
)
}§Interoperability
This implementation makes use of the browser’s history API. While it is possible to receive the current state
from the History API, and trigger “back” operations, using web_sys::History::push_state directly will not
trigger an event and thus no render a different page.
As gloo_history creates its internal type and state system, it is not interoperable with this crate. It still is
possible to use gloo_utils::history though, which is just a shortcut of getting web_sys::History.
§More examples
See the examples folder.
Modules§
- components
- Some out-of-the box components.
- prelude
- Common includes.
- target
- Routing target
Structs§
- History
- Router
- Top-level router component.
- Scope
- A component, translating down to the next level.
- Switch
- A component two switch rendering between the different targets.
Derive Macros§
- Target
- Helps implementing the
Targettrait in an enum.