diff --git a/docs/router/src/README.md b/docs/router/src/README.md new file mode 100644 index 000000000..fcbd79aef --- /dev/null +++ b/docs/router/src/README.md @@ -0,0 +1,10 @@ +# Dioxus Router: Introduction +Whether or not you're building a website, desktop app, or mobile app, organizing your app's views into "pages" can be an effective method for organization and maintainability. + +Dioxus comes with a router built-in. To start utilizing Dioxus Router, enable the ``router`` feature in your ``Cargo.toml`` file. +```toml +[dependencies] +dioxus = { version = "x.x.x", features = [.., "router"] } +``` + +In this book you'll find a short [guide](./guide/index.md) to get up to speed with Dioxus Router, as well as the router's [reference](./reference/index.md). \ No newline at end of file diff --git a/docs/router/src/SUMMARY.md b/docs/router/src/SUMMARY.md index 7390c8289..dc8bde93a 100644 --- a/docs/router/src/SUMMARY.md +++ b/docs/router/src/SUMMARY.md @@ -1,3 +1,11 @@ # Summary -- [Chapter 1](./chapter_1.md) +- [Introduction](./README.md) + +- [Guide](./guide/index.md) + - [Getting Started](./guide/getting-started.md) + - [Creating Our First Route](./guide/first-route.md) + - [Building a Nest](./guide/building-a-nest.md) + - [Redirection Perfection](./guide/redirection-perfection.md) +- [Reference](./reference/index.md) + - [X]() \ No newline at end of file diff --git a/docs/router/src/chapter_1.md b/docs/router/src/chapter_1.md deleted file mode 100644 index d4a45381b..000000000 --- a/docs/router/src/chapter_1.md +++ /dev/null @@ -1,3 +0,0 @@ -# Chapter 1 - -The Dioxus Router is very important!! diff --git a/docs/router/src/guide/building-a-nest.md b/docs/router/src/guide/building-a-nest.md new file mode 100644 index 000000000..5abe6ede7 --- /dev/null +++ b/docs/router/src/guide/building-a-nest.md @@ -0,0 +1,201 @@ +# 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 outside of routes. + +### 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: +```rs +fn navbar(cx: Scope) -> Element { + cx.render(rsx! { + 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 unnecessarily reload. Instead we want to use the ``Link`` component provided by Dioxus Router. + +The Link component is very similar to the Route component. It takes a path and an element. Add the Link component into your use statement and then add some links: +```rs +use dioxus::{ + prelude::*, + router::{Route, Router, Link}, // UPDATED +}; + +... + +fn navbar(cx: Scope) -> Element { + cx.render(rsx! { + ul { + // NEW + Link { to: "/", "Home"} + br {} + Link { to: "/blog", "Blog"} + } + }) +} +``` +>By default, the Link component only works for links within your application. To link to external sites, add the ``external: true`` property. +>```rs +>Link { to: "https://github.com", external: true, "GitHub"} +>``` + +And finally, use the navbar component in your app component: +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router { + p { "-- Dioxus Blog --" } + self::navbar {} // NEW + Route { to: "/", self::homepage {}} + Route { to: "", self::page_not_found {}} + } + }) +} +``` +Now you should see a list of links near the top of your page. Click on one and you should seamlessly travel between pages. + +##### WIP: Active Link Styling + +### URL Parameters and Nested Routes +Many websites such as GitHub put parameters in their URL. For example, ``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. And finally, we also want our site to tell users they are on a blog page whenever the URL starts with``/blog``. + +The path to our blog will look like ``/blog/myBlogPage``. ``myBlogPage`` being the URL parameter. +Dioxus Router uses the ``:name`` pattern so our route will look like ``/blog/:post``. + +First, lets tell users when they are on a blog page. Add a new route in your app component. +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router { + p { "-- Dioxus Blog --" } + self::navbar {} + Route { to: "/", self::homepage {}} + // NEW + Route { + to: "/blog", + } + Route { to: "", self::page_not_found {}} + } + }) +} +``` +Routes can take components as parameters and we know that a route is a component. We nest routes by doing exactly what they are called, nesting them: +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router { + p { "-- Dioxus Blog --" } + self::navbar {} + Route { to: "/", self::homepage {}} + Route { + to: "/blog", + Route { to: "/:post", "This is my blog post!" } // NEW + } + Route { to: "", self::page_not_found {}} + } + }) +} +``` +Nesting our route like this isn't too helpful at first, but remember we want to tell users they are on a blog page. Let's move our ``p { "-- Dioxus Blog --" }`` inside of our ``/blog`` route. +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router { + self::navbar {} + Route { to: "/", self::homepage {}} + Route { + to: "/blog", + p { "-- Dioxus Blog --" } // MOVED + Route { to: "/:post", "This is my blog post!" } + } + Route { to: "", self::page_not_found {}} + } + }) +} +``` +Now our ``-- Dioxus Blog --`` text will be displayed whenever a user is on a path that starts with ``/blog``. Displaying content in a way that is page-agnostic is useful when building navigation menus, footers, and similar. + +All that's left is to handle our URL parameter. We will begin by creating a ``get_blog_post`` function. In a real site, this function would call an API endpoint to get a blog post from the database. However, that is out of the scope of this guide so we will be utilizing static text. +```rs +fn get_blog_post(id: &str) -> String { + match id { + "foo" => "Welcome to the foo blog post!".to_string(), + "bar" => "This is the bar blog post!".to_string(), + id => format!("Blog post '{id}' does not exist!") + } +} + +``` +Now that we have established our helper function, lets create a new ``blog_post`` component. +```rs +fn blog_post(cx: Scope) -> Element { + let blog_text = ""; + + cx.render(rsx! { + p { "{blog_text}" } + }) +} +``` +All that's left is to extract the blog id from the URL and to call our helper function to get the blog text. To do this we need to utilize Dioxus Router's ``use_route`` hook. +First start by adding ``use_route`` to your imports and then utilize the hook in your ``blog_post`` component. +```rs +use dioxus::{ + prelude::*, + router::{use_route, Link, Route, Router}, // UPDATED +}; + +... + +fn blog_post(cx: Scope) -> Element { + let route = use_route(&cx); // NEW + let blog_text = ""; + + cx.render(rsx! { + p { "{blog_text}" } + }) +} +``` +Dioxus Router provides built in methods to extract information from a route. We could utilize the ``segments``, ``nth_segment``, or ``last_segment`` method for our case but we'll use the ``segment`` method which extracts a specific URL parameter. +The ``segment`` method also parses the parameter into any type for us. We'll use a match expression that handles a parsing error and on success, uses our helper function to grab the blog post. +```rs +fn blog_post(cx: Scope) -> Element { + let route = use_route(&cx); + + // NEW + let blog_text = match route.segment::("post").unwrap() { + Ok(val) => get_blog_post(&val), + Err(_) => "An unknown error occured".to_string(), + }; + + cx.render(rsx! { + p { "{blog_text}" } + }) +} +``` +And finally add the ``blog_post`` component to your ``app`` component: +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router { + self::navbar {} + Route { to: "/", self::homepage {}} + Route { + to: "/blog", + p { "-- Dioxus Blog --" } + Route { to: "/:post", self::blog_post {} } // UPDATED + } + Route { to: "", self::page_not_found {}} + } + }) +} +``` +That's it! If you head to ``/blog/foo`` you should see ``Welcome to the foo blog 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 and final chapter, we will go over the ``Redirect`` component to redirect non-authorized users to another page. \ No newline at end of file diff --git a/docs/router/src/guide/first-route.md b/docs/router/src/guide/first-route.md new file mode 100644 index 000000000..75740a739 --- /dev/null +++ b/docs/router/src/guide/first-route.md @@ -0,0 +1,95 @@ +# Creating Our First Route +In this chapter, we will continue off of our new Dioxus project to create a homepage and start utilizing Dioxus Router! + +### Fundamentals +Dioxus Router works based on a router and route component. If you've ever used [React Router](https://reactrouter.com/), you should feel at home with Dioxus Router. + +To get started, import the ``Router`` and ``Route`` components. +```rs +use dioxus::{ + prelude::*, + router::{Route, Router} +} +``` +We also need an actual page to route to! Add a homepage component: +```rs +fn homepage(cx: Scope) -> Element { + cx.render(rsx! { + p { "Welcome to Dioxus Blog!" } + }) +} +``` + +### To Route or Not to Route +We want to use Dioxus Router to seperate our application into different "pages". Dioxus Router will then determine which page to render based on the URL path. + +To start using Dioxus Router, we need to use the ``Router`` component. +Replace the ``p { "Hello, wasm!" }`` in your ``app`` component with a Router component: +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router {} // NEW + }) +} +``` +Now we have established a router and we can create our first route. We will be creating a route for our homepage component we created earlier. +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router { + Route { to: "/", self::homepage {}} // NEW + } + }) +} +``` +If you head to your application's browser tab, you should see the text ``Welcome to Dioxus Blog!`` when on the root URL (``http://localhost:8080/``). If you enter a different path for the URL, nothing should be displayed. + +This is because we told Dioxus Router to render the ``homepage`` component only when the URL path is ``/``. You can tell Dioxus Router to render any kind of component such as a ``div {}``. + +### What if a Route Doesn't Exist? +In our example Dioxus Router doesn't render anything. If we wanted to, we could tell Dioxus Router to render a component all the time! Try it out: +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router { + p { "-- Dioxus Blog --" } // NEW + Route { to: "/", self::homepage {}} + } + }) +} +``` +We will go into more detail about this in the next chapter. + +Many sites also have a "404" page for when a URL path leads to nowhere. Dioxus Router can do this too! Create a new ``page_not_found`` component. +```rs +fn page_not_found(cx: Scope) -> Element { + cx.render(rsx! { + p { "Oops! The page you are looking for doesn't exist!" } + }) +} +``` + +Now to tell Dioxus Router to render our new component when no route exists. Create a new route with a path of nothing: +```rs +fn app(cx: Scope) -> Element { + cx.render(rsx! { + Router { + p { "-- Dioxus Blog --" } + Route { to: "/", self::homepage {}} + Route { to: "", self::page_not_found {}} // NEW + } + }) +} +``` +Now when you go to a route that doesn't exist, you should see the page not found text and the text we told Dioxus Router to render all the time. +``` +// localhost:8080/abc + +-- Dioxus Blog -- +Oops! The page you are looking for doesn't exist! +``` + +> Make sure you put your empty route at the bottom or else it'll override any routes below it! + +### Conclusion +In this chapter we learned how to create a route and tell Dioxus Router what component to render when the URL path is equal to what we specified. We also created a 404 page to handle when a route doesn't exist. Next, we'll create the blog portion of our site. We will utilize nested routes and URL parameters. \ No newline at end of file diff --git a/docs/router/src/guide/getting-started.md b/docs/router/src/guide/getting-started.md new file mode 100644 index 000000000..a63f430b2 --- /dev/null +++ b/docs/router/src/guide/getting-started.md @@ -0,0 +1,68 @@ +# Getting Started +Before we start utilizing Dioxus Router, we need to initialize a Dioxus web application. + +#### Required Tools +If you haven't already, make sure you install the [trunk](https://trunkrs.dev/) build tool and the rust ``wasm32-unknown-unknown`` target: +``` +$ cargo install trunk + ... +$ rustup target add wasm32-unkown-unknown + ... +``` + +### Creating the Project +First, create a new cargo binary project: +``` +cargo new --bin dioxus-blog +``` + +Next, we need to add dioxus with the web and router feature to our ``Cargo.toml`` file. +```toml +[package] +name = "dioxus-blog" +version = "0.1.0" +edition = "2021" + +[dependencies] +dioxus = { version = "0.1.8", features = ["web", "router"] } +``` + +Now we can start coding! Create an ``index.html`` file in the root of your project: +```html + + + Dioxus Blog + + +
+ + +``` +You can add whatever you want to this file, just ensure that you have a ``div`` with the id of ``main`` in the root of your body element. This is essentially a handle to where Dioxus will render your components. + +Now move to ``src/main.rs`` and replace its contents with: +```rs +use dioxus::prelude::*; + +fn main() { + // Launch Dioxus web app + dioxus::web::launch(app); +} + +// Our root component. +fn app(cx: Scope) -> Element { + // Render "Hello, wasm!" to the screen. + cx.render(rsx! { + p { "Hello, wasm!"} + }) +} +``` + +Our project is now setup! To make sure everything is running correctly, in the root of your project run: +``` +trunk serve +``` +Then head to [http://localhost:8080](http://localhost:8080) in your browser, and you should see ``Hello, wasm!`` on your screen. + +#### Conclusion +We setup a new project with Dioxus and got everything running correctly. Next we'll create a small homepage and start our journey with Dioxus Router. \ No newline at end of file diff --git a/docs/router/src/guide/index.md b/docs/router/src/guide/index.md new file mode 100644 index 000000000..29c8049ee --- /dev/null +++ b/docs/router/src/guide/index.md @@ -0,0 +1,14 @@ +# Dioxus Router: Guide +In this guide you'll learn to effectively use Dioxus Router whether you're building a small todo app or the next FAANG company. We will create a small website with a blog, homepage, and more! + +#### You'll learn how to +- Create routes and render "pages". +- Utilize nested routes, create a navigation bar, and render content for a set of routes. +- Gather URL parameters to dynamically display content. +- Redirect your visitors wherever you want. + +> Disclaimer +> +> This site will only display the features of Dioxus Router. It will not include any actual functionality. To keep things simple we will only be using a single file, this is not the recommended way of doing things with a real application. + +You can find the complete application [here](https://github.com/DogeDark/dioxus-router-example). \ No newline at end of file diff --git a/docs/router/src/guide/redirection-perfection.md b/docs/router/src/guide/redirection-perfection.md new file mode 100644 index 000000000..4f42c2b73 --- /dev/null +++ b/docs/router/src/guide/redirection-perfection.md @@ -0,0 +1,51 @@ +# Redirection Perfection +You're well on your way to becoming a routing master! + +In this chapter we will cover utilizing the ``Redirect`` component so you can take Rickrolling to the next level. We will also provide some optional challenges at the end if you want to continue your practice with not only Dioxus Router but with Dioxus in general. + +### What Is This Redirect Thing? +The ``Redirect`` component is simple! When Dioxus determines that it should be rendered, it will redirect your application visitor to wherever you want. +In this example, let's say that you added a secret page to your site but didn't have time to program in the permission system. As a quick fix you add a redirect. + +As always, let's first create a new component named ``secret_page``. +```rs +fn secret_page(cx: Scope) -> Element { + cx.render(rsx! { + p { "This page is not to be viewed!" } + }) +} +``` +To redirect our visitors, all we have to do is render the ``Redirect`` component. The ``Redirect`` component is very similar to the ``Link`` component. The main difference is it doesn't display anything new. +First import the ``Redirect`` component and then update your ``secret_page`` component: +```rs +use dioxus::{ + prelude::*, + router::{use_route, Link, Redirect, Route, Router}, // UPDATED +}; + +... + +fn secret_page(cx: Scope) -> Element { + cx.render(rsx! { + p { "This page is not to be viewed!" } + Redirect { to: "/" } // NEW + }) +} +``` +That's it! Now your users will be redirected away from the secret page. + +>Similar to the ``Link`` component, the ``Redirect`` component needs to be explicitly set to redirect to an external site. To link to external sites, add the ``external: true`` property. +>```rs +>Redirect { to: "https://github.com", external: true} +>``` + +### Conclusion +Well done! You've completed the Dioxus Router guide book. You've built a small application and learned about the many things you can do with Dioxus Router. To continue your journey, you can find a list of challenges down below, or you can check out the [reference](../reference/index.md). + +### Challenges +- Organize your components into seperate files for better maintainability. +- Give your app some style if you haven't already. +- Build an about page so your visitors know who you are. +- Add a user system that uses URL parameters. +- Create a simple admin system to create, delete, and edit blogs. +- If you want to go to the max, hook up your application to a rest API and database. \ No newline at end of file diff --git a/docs/router/src/reference/index.md b/docs/router/src/reference/index.md new file mode 100644 index 000000000..1d29fce21 --- /dev/null +++ b/docs/router/src/reference/index.md @@ -0,0 +1,2 @@ +# Dioxus Router: Reference +This section includes a reference to Dioxus Router's API and functionality. \ No newline at end of file