Crate yew_nested_router
source ·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::Overview
target. - 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 P
arent -> C
hild.
#[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§
- Some out-of-the box components.
- Common includes.
- Routing target
Structs§
- Top-level router component.
- A component, translating down to the next level.
- A component two switch rendering between the different targets.
Derive Macros§
- Helps implementing the
Target
trait in an enum.