simple_routes.rs 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216
  1. use dioxus::prelude::*;
  2. use dioxus_router::prelude::*;
  3. use std::str::FromStr;
  4. #[cfg(feature = "liveview")]
  5. #[tokio::main]
  6. async fn main() {
  7. use axum::{extract::ws::WebSocketUpgrade, response::Html, routing::get, Router};
  8. let listen_address: std::net::SocketAddr = ([127, 0, 0, 1], 3030).into();
  9. let view = dioxus_liveview::LiveViewPool::new();
  10. let app = Router::new()
  11. .fallback(get(move || async move {
  12. Html(format!(
  13. r#"
  14. <!DOCTYPE html>
  15. <html>
  16. <head></head>
  17. <body><div id="main"></div></body>
  18. {glue}
  19. </html>
  20. "#,
  21. glue = dioxus_liveview::interpreter_glue(&format!("ws://{listen_address}/ws"))
  22. ))
  23. }))
  24. .route(
  25. "/ws",
  26. get(move |ws: WebSocketUpgrade| async move {
  27. ws.on_upgrade(move |socket| async move {
  28. _ = view
  29. .launch(dioxus_liveview::axum_socket(socket), Root)
  30. .await;
  31. })
  32. }),
  33. );
  34. println!("Listening on http://{listen_address}");
  35. axum::Server::bind(&listen_address.to_string().parse().unwrap())
  36. .serve(app.into_make_service())
  37. .await
  38. .unwrap();
  39. }
  40. #[cfg(not(feature = "liveview"))]
  41. fn main() {
  42. #[cfg(not(target_arch = "wasm32"))]
  43. dioxus_desktop::launch(Root);
  44. #[cfg(target_arch = "wasm32")]
  45. dioxus_web::launch(root);
  46. }
  47. #[cfg(feature = "liveview")]
  48. #[component]
  49. fn Root(cx: Scope) -> Element {
  50. let history = LiveviewHistory::new(cx);
  51. render! { Router::<Route> { config: || RouterConfig::default().history(history) } }
  52. }
  53. #[cfg(not(feature = "liveview"))]
  54. #[component]
  55. fn Root(cx: Scope) -> Element {
  56. render! { Router::<Route> {} }
  57. }
  58. #[component]
  59. fn UserFrame(cx: Scope, user_id: usize) -> Element {
  60. render! {
  61. pre { "UserFrame{{\n\tuser_id:{user_id}\n}}" }
  62. div { background_color: "rgba(0,0,0,50%)",
  63. "children:"
  64. Outlet::<Route> {}
  65. }
  66. }
  67. }
  68. #[component]
  69. fn Route1(cx: Scope, user_id: usize, dynamic: usize, query: String, extra: String) -> Element {
  70. render! {
  71. pre {
  72. "Route1{{\n\tuser_id:{user_id},\n\tdynamic:{dynamic},\n\tquery:{query},\n\textra:{extra}\n}}"
  73. }
  74. Link {
  75. to: Route::Route1 {
  76. user_id: *user_id,
  77. dynamic: *dynamic,
  78. query: String::new(),
  79. extra: extra.clone() + ".",
  80. },
  81. "Route1 with extra+\".\""
  82. }
  83. p { "Footer" }
  84. Link {
  85. to: Route::Route3 {
  86. dynamic: String::new(),
  87. },
  88. "Home"
  89. }
  90. }
  91. }
  92. #[component]
  93. fn Route2(cx: Scope, user_id: usize) -> Element {
  94. render! {
  95. pre { "Route2{{\n\tuser_id:{user_id}\n}}" }
  96. (0..*user_id).map(|i| rsx!{ p { "{i}" } }),
  97. p { "Footer" }
  98. Link {
  99. to: Route::Route3 {
  100. dynamic: String::new(),
  101. },
  102. "Home"
  103. }
  104. }
  105. }
  106. #[component]
  107. fn Route3(cx: Scope, dynamic: String) -> Element {
  108. let current_route = use_route(cx)?;
  109. let current_route_str = use_ref(cx, String::new);
  110. let parsed = Route::from_str(&current_route_str.read());
  111. let site_map = Route::SITE_MAP
  112. .iter()
  113. .flat_map(|seg| seg.flatten().into_iter())
  114. .collect::<Vec<_>>();
  115. let navigator = use_navigator(cx);
  116. render! {
  117. input {
  118. oninput: move |evt| {
  119. *current_route_str.write() = evt.value();
  120. },
  121. value: "{current_route_str.read()}"
  122. }
  123. "dynamic: {dynamic}"
  124. Link { to: Route::Route2 { user_id: 8888 }, "hello world link" }
  125. button {
  126. disabled: !navigator.can_go_back(),
  127. onclick: move |_| {
  128. navigator.go_back();
  129. },
  130. "go back"
  131. }
  132. button {
  133. disabled: !navigator.can_go_forward(),
  134. onclick: move |_| {
  135. navigator.go_forward();
  136. },
  137. "go forward"
  138. }
  139. button {
  140. onclick: move |_| {
  141. navigator.push("https://www.google.com");
  142. },
  143. "google link"
  144. }
  145. p { "Site Map" }
  146. pre { "{site_map:#?}" }
  147. p { "Dynamic link" }
  148. match parsed {
  149. Ok(route) => {
  150. if route != current_route {
  151. render! {
  152. Link {
  153. to: route.clone(),
  154. "{route}"
  155. }
  156. }
  157. }
  158. else {
  159. None
  160. }
  161. }
  162. Err(err) => {
  163. render! {
  164. pre {
  165. color: "red",
  166. "Invalid route:\n{err}"
  167. }
  168. }
  169. }
  170. }
  171. }
  172. }
  173. #[rustfmt::skip]
  174. #[derive(Clone, Debug, PartialEq, Routable)]
  175. enum Route {
  176. #[nest("/test")]
  177. // Nests with parameters have types taken from child routes
  178. #[nest("/user/:user_id")]
  179. // Everything inside the nest has the added parameter `user_id: usize`
  180. // UserFrame is a layout component that will receive the `user_id: usize` parameter
  181. #[layout(UserFrame)]
  182. #[route("/:dynamic?:query")]
  183. Route1 {
  184. // The type is taken from the first instance of the dynamic parameter
  185. user_id: usize,
  186. dynamic: usize,
  187. query: String,
  188. extra: String,
  189. },
  190. #[route("/hello_world")]
  191. // You can opt out of the layout by using the `!` prefix
  192. #[layout(!UserFrame)]
  193. Route2 { user_id: usize },
  194. #[end_layout]
  195. #[end_nest]
  196. #[end_nest]
  197. #[redirect("/:id/user", |id: usize| Route::Route3 { dynamic: id.to_string()})]
  198. #[route("/:dynamic")]
  199. Route3 { dynamic: String },
  200. }