update the router guide
This commit is contained in:
1 parent
67992f7da9
commit
0d3ea377ce
63 files changed
+615
-1705
No files matched your search
@@ -1,137 +1,60 @@
|
||||
# Building a Nest
|
||||
Not a bird's nest! A nest of routes!
|
||||
|
||||
In this chapter we will begin to build the blog portion of our site which will
|
||||
include links, nested URLs, and URL parameters. We will also explore the use
|
||||
case of rendering components directly in the component calling [`use_router`].
|
||||
In this chapter, we will begin to build the blog portion of our site which will
|
||||
include links, nested routes, and route parameters.
|
||||
|
||||
## Site Navigation
|
||||
|
||||
Our site visitors won't know all the available pages and blogs on our site so we
|
||||
should provide a navigation bar for them.
|
||||
should provide a navigation bar for them. Our navbar will be a list of links going between our pages.
|
||||
|
||||
We want our navbar component to be rendered on several different pages on our site. Instead of duplicating the code, we can create a component that wraps all children routes. This is called a layout component. To tell the router where to render the child routes, we use the [`Outlet`] component.
|
||||
|
||||
Let's create a new `NavBar` component:
|
||||
```rust,no_run
|
||||
# // Hidden lines (like this one) make the documentation tests work.
|
||||
# extern crate dioxus;
|
||||
# use dioxus::prelude::*;
|
||||
# extern crate dioxus_router;
|
||||
# use dioxus_router::prelude::*;
|
||||
#
|
||||
fn NavBar(cx: Scope) -> Element {
|
||||
render! {
|
||||
nav {
|
||||
ul { }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```rust, no_run
|
||||
{{#include ../../examples/nested_routes.rs:nav}}
|
||||
```
|
||||
|
||||
Our navbar will be a list of links going between our pages. We could always use
|
||||
an HTML anchor element but that would cause our page to reload unnecessarily.
|
||||
Instead we want to use the [`Link`] component provided by Dioxus Router.
|
||||
Next, let's add our `NavBar` component as a layout to our Route enum:
|
||||
|
||||
The [`Link`] is similar to a regular `a` tag. It takes a target (for now a path,
|
||||
more on other targets later) and an element. Let's add our links
|
||||
```rust, no_run
|
||||
{{#include ../../examples/nested_routes.rs:router}}
|
||||
```
|
||||
|
||||
```rust,no_run
|
||||
# // Hidden lines (like this one) make the documentation tests work.
|
||||
# extern crate dioxus;
|
||||
# use dioxus::prelude::*;
|
||||
# extern crate dioxus_router;
|
||||
# use dioxus_router::prelude::*;
|
||||
#
|
||||
fn NavBar(cx: Scope) -> Element {
|
||||
render! {
|
||||
nav {
|
||||
ul {
|
||||
// new stuff starts here
|
||||
li { Link {
|
||||
target: NavigationTarget::Internal(String::from("/")),
|
||||
"Home"
|
||||
} }
|
||||
li { Link {
|
||||
target: "/blog", // short form
|
||||
"Blog"
|
||||
} }
|
||||
// new stuff ends here
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
To add links to our `NavBar`, we could always use an HTML anchor element but that has two issues:
|
||||
|
||||
1. It causes a full-page reload
|
||||
2. We can accidentally link to a page that doesn't exist
|
||||
|
||||
Instead, we want to use the [`Link`] component provided by Dioxus Router.
|
||||
|
||||
The [`Link`] is similar to a regular `<a>` tag. It takes a target and children.
|
||||
|
||||
Unlike a regular `<a>` tag, we can pass in our Route enum as the target. Because we annotated our routes with the [`route(path)`] attribute, the [`Link`] will know how to generate the correct URL. If we use the Route enum, the rust compiler will prevent us from linking to a page that doesn't exist.
|
||||
|
||||
Let's add our links:
|
||||
|
||||
```rust, no_run
|
||||
{{#include ../../examples/links.rs:nav}}
|
||||
```
|
||||
|
||||
> Using this method, the [`Link`] component only works for links within our
|
||||
> application. To learn more about navigation targets see
|
||||
> [here](./navigation-targets.md).
|
||||
|
||||
And finally, we add the navbar component in our app component:
|
||||
```rust,no_run
|
||||
# // Hidden lines (like this one) make the documentation tests work.
|
||||
# extern crate dioxus;
|
||||
# use dioxus::prelude::*;
|
||||
# extern crate dioxus_router;
|
||||
# use dioxus_router::prelude::*;
|
||||
# fn Home(cx: Scope) -> Element { unimplemented!() }
|
||||
# fn NavBar(cx: Scope) -> Element { unimplemented!() }
|
||||
# fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
|
||||
#
|
||||
fn App(cx: Scope) -> Element {
|
||||
use_router(
|
||||
cx,
|
||||
&|| RouterConfiguration::default(),
|
||||
&|| Segment::content(comp(Home)).fallback(comp(PageNotFound))
|
||||
);
|
||||
|
||||
render! {
|
||||
NavBar { } // this is new
|
||||
Outlet { }
|
||||
}
|
||||
}
|
||||
```
|
||||
Now you should see a list of links near the top of your page. Click on one and
|
||||
you should seamlessly travel between pages.
|
||||
|
||||
### Active Link Styling
|
||||
You might want to style links differently, when their page is currently open.
|
||||
To achieve this, we can tell the [`Link`] to give its internal `a` tag a class
|
||||
in that case.
|
||||
|
||||
```rust,no_run
|
||||
# // Hidden lines (like this one) make the documentation tests work.
|
||||
# extern crate dioxus;
|
||||
# use dioxus::prelude::*;
|
||||
# extern crate dioxus_router;
|
||||
# use dioxus_router::prelude::*;
|
||||
#
|
||||
fn NavBar(cx: Scope) -> Element {
|
||||
render! {
|
||||
nav {
|
||||
ul {
|
||||
li { Link {
|
||||
target: NavigationTarget::Internal(String::from("/")),
|
||||
active_class: "active", // this is new
|
||||
"Home"
|
||||
} }
|
||||
li { Link {
|
||||
target: "/blog",
|
||||
active_class: "active", // this is new
|
||||
"Blog"
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
> This will not be reflected in the [full example code](./full-code.md).
|
||||
|
||||
## URL Parameters and Nested Routes
|
||||
|
||||
Many websites such as GitHub put parameters in their URL. For example,
|
||||
`https://github.com/DioxusLabs` utilizes the text after the domain to
|
||||
dynamically search and display content about an organization.
|
||||
|
||||
We want to store our blogs in a database and load them as needed. This'll help
|
||||
prevent our app from being bloated therefor providing faster load times. We also
|
||||
We want to store our blogs in a database and load them as needed. We also
|
||||
want our users to be able to send people a link to a specific blog post.
|
||||
Instead of listing all of the blog titles at compile time, we can make a dynamic route.
|
||||
|
||||
We could utilize a search page that loads a blog when clicked but then our users
|
||||
won't be able to share our blogs easily. This is where URL parameters come in.
|
||||
@@ -139,124 +62,38 @@ won't be able to share our blogs easily. This is where URL parameters come in.
|
||||
The path to our blog will look like `/blog/myBlogPage`, `myBlogPage` being the
|
||||
URL parameter.
|
||||
|
||||
First, lets create a component that wraps around all blog content. This allows
|
||||
us to add a heading that tells the user they are on the blog.
|
||||
```rust,no_run
|
||||
# // Hidden lines (like this one) make the documentation tests work.
|
||||
# extern crate dioxus;
|
||||
# use dioxus::prelude::*;
|
||||
# extern crate dioxus_router;
|
||||
# use dioxus_router::prelude::*;
|
||||
#
|
||||
fn Blog(cx: Scope) -> Element {
|
||||
render! {
|
||||
h1 { "Blog" }
|
||||
Outlet {}
|
||||
}
|
||||
}
|
||||
```
|
||||
First, let's create a layout component (similar to the navbar) that wraps the blog content. This allows us to add a heading that tells the user they are on the blog.
|
||||
|
||||
> Note the `Outlet { }` component. For the components of a nested route to be
|
||||
> rendered, we need an equally nested outlet. For more details, see the
|
||||
> [nested routes](../features/routes/nested.md) chapter of the features section.
|
||||
```rust, no_run
|
||||
{{#include ../../examples/dynamic_route.rs:blog}}
|
||||
```
|
||||
|
||||
Now we'll create another index component, that'll be displayed when no blog post
|
||||
is selected:
|
||||
```rust,no_run
|
||||
# // Hidden lines (like this one) make the documentation tests work.
|
||||
# extern crate dioxus;
|
||||
# use dioxus::prelude::*;
|
||||
# extern crate dioxus_router;
|
||||
# use dioxus_router::prelude::*;
|
||||
#
|
||||
fn BlogList(cx: Scope) -> Element {
|
||||
render! {
|
||||
h2 { "Choose a post" }
|
||||
ul {
|
||||
li { Link {
|
||||
target: "/blog/1",
|
||||
"Read the first blog post"
|
||||
} }
|
||||
li { Link {
|
||||
target: "/blog/2",
|
||||
"Read the second blog post"
|
||||
} }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
```rust, no_run
|
||||
{{#include ../../examples/dynamic_route.rs:blog_list}}
|
||||
```
|
||||
|
||||
We also need to create a component that displays an actual blog post. Within
|
||||
this component we can use the `use_route` hook to gain access to our URL
|
||||
parameters:
|
||||
```rust,no_run
|
||||
# // Hidden lines (like this one) make the documentation tests work.
|
||||
# extern crate dioxus;
|
||||
# use dioxus::prelude::*;
|
||||
# extern crate dioxus_router;
|
||||
# use dioxus_router::prelude::*;
|
||||
#
|
||||
struct PostId;
|
||||
We also need to create a component that displays an actual blog post. This component will accept the URL parameters as props:
|
||||
|
||||
fn BlogPost(cx: Scope) -> Element {
|
||||
let route = use_route(cx).unwrap();
|
||||
|
||||
let post_id = route.parameter::<PostId>();
|
||||
let post = post_id
|
||||
.map(|id| id.to_string())
|
||||
.unwrap_or(String::from("unknown"));
|
||||
|
||||
render! {
|
||||
h2 { "Blog Post: {post}"}
|
||||
}
|
||||
}
|
||||
```rust, no_run
|
||||
{{#include ../../examples/dynamic_route.rs:blog_post}}
|
||||
```
|
||||
|
||||
Finally, let's tell our router about those components.
|
||||
```rust,no_run
|
||||
# // Hidden lines (like this one) make the documentation tests work.
|
||||
# extern crate dioxus;
|
||||
# use dioxus::prelude::*;
|
||||
# extern crate dioxus_router;
|
||||
# use dioxus_router::prelude::*;
|
||||
# fn Blog(cx: Scope) -> Element { unimplemented!() }
|
||||
# fn BlogList(cx: Scope) -> Element { unimplemented!() }
|
||||
# struct PostId;
|
||||
# fn BlogPost(cx: Scope) -> Element { unimplemented!() }
|
||||
# fn Home(cx: Scope) -> Element { unimplemented!() }
|
||||
# fn NavBar(cx: Scope) -> Element { unimplemented!() }
|
||||
# fn PageNotFound(cx: Scope) -> Element { unimplemented!() }
|
||||
#
|
||||
fn App(cx: Scope) -> Element {
|
||||
use_router(
|
||||
cx,
|
||||
&|| RouterConfiguration::default(),
|
||||
&|| {
|
||||
Segment::content(comp(Home))
|
||||
// new stuff starts here
|
||||
.fixed("blog", Route::content(comp(Blog)).nested(
|
||||
Segment::content(comp(BlogList))
|
||||
.catch_all((comp(BlogPost), PostId { }))
|
||||
))
|
||||
// new stuff ends here
|
||||
.fallback(comp(PageNotFound))
|
||||
}
|
||||
);
|
||||
Finally, let's tell our router about those components:
|
||||
|
||||
render! {
|
||||
NavBar { }
|
||||
Outlet { }
|
||||
}
|
||||
}
|
||||
```rust, no_run
|
||||
{{#include ../../examples/dynamic_route.rs:router}}
|
||||
```
|
||||
|
||||
That's it! If you head to `/blog/1` you should see our sample post.
|
||||
|
||||
## Conclusion
|
||||
In this chapter we utilized Dioxus Router's Link, URL Parameter, and `use_route`
|
||||
|
||||
In this chapter, we utilized Dioxus Router's Link, and Route Parameter
|
||||
functionality to build the blog portion of our application. In the next chapter,
|
||||
we will go over how navigation targets (like the one we passed to our links)
|
||||
work.
|
||||
|
||||
[`Link`]: https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Link.html
|
||||
[`use_router`]: https://docs.rs/dioxus-router/latest/dioxus_router/hooks/fn.use_router.html
|
||||
[`Link`]: https://docs.rs/dioxus-router/latest/dioxus_router/prelude/fn.GenericLink<R>.html
|
||||
Reference in new issue
Block a user