add rewritten router book and fix features code

This commit is contained in:
Adrian Wannenmacher committed 2022-12-15 23:36:02 +01:00
1 parent 911e878d83
commit e3610738ab
38 files changed
+2909 -449

No files matched your search

+263
View File
@@ -0,0 +1,263 @@
# 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 [`Router`].
## 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.
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 {
cx.render(rsx! {
nav {
ul { }
}
})
}
```
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.
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
# // 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 {
cx.render(rsx! {
nav {
ul {
// new stuff starts here
li { Link { target: InternalTarget(String::from("/")), "Home" } }
li { Link {
target: "/blog", // short form
"Blog"
} }
// new stuff ends here
}
}
})
}
```
> 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 {
let routes = use_segment(&cx, || {
Segment::new()
.index(Home as Component)
.fallback(PageNotFound as Component)
});
cx.render(rsx! {
Router {
routes: routes.clone(),
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 {
cx.render(rsx! {
nav {
ul {
li { Link {
target: InternalTarget(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
want our users to be able to send people a link to a specific blog post.
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.
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 {
cx.render(rsx! {
h1 { "Blog" }
Outlet {}
})
}
```
> 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.
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 {
cx.render(rsx! {
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"
} }
}
})
}
```
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::*;
#
fn BlogPost(cx: Scope) -> Element {
let route = use_route(&cx).unwrap();
let post_id = route.parameters.get("post_id");
let post = post_id
.map(|id| id.to_string())
.unwrap_or(String::from("unknown"));
cx.render(rsx! {
h2 { "Blog Post: {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!() }
# 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 {
let routes = use_segment(&cx, || {
Segment::default()
.index(Home as Component)
// new stuff starts here
.fixed(
"blog",
Route::new(Blog as Component).nested(
Segment::default()
.index(BlogList as Component)
.catch_all(("post_id", BlogPost as Component))
),
)
// new stuff ends here
.fallback(PageNotFound as Component)
});
cx.render(rsx! {
Router {
routes: routes.clone(),
NavBar { }
Outlet { }
}
})
}
```
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`
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
[`Router`]: https://docs.rs/dioxus-router/latest/dioxus_router/components/fn.Router.html