simple_routes.rs 5.5 KB

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