Updating `todo-app-sqlite` example (todo: fix hydration)

This commit is contained in:
Greg Johnston 2022-12-19 16:25:24 -05:00
parent d6920847ca
commit 8be33ccd7c
5 changed files with 92 additions and 82 deletions

View File

@ -27,6 +27,7 @@ reqwest = { version = "0.11", features = ["json"] }
# openssl = { version = "0.10", features = ["v110"] }
wasm-bindgen = "0.2"
web-sys = { version = "0.3", features = ["AbortController", "AbortSignal"] }
tracing = "0.1"
[features]
default = ["csr"]

1
examples/todo-app-sqlite/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
.leptos.kdl

View File

@ -12,9 +12,8 @@ cfg_if! {
pub fn hydrate() {
console_error_panic_hook::set_once();
_ = console_log::init_with_level(log::Level::Debug);
console_error_panic_hook::set_once();
leptos::hydrate(body().unwrap(), |cx| {
leptos::mount_to_body(|cx| {
view! { cx, <TodoApp/> }
});
}

View File

@ -9,6 +9,7 @@ cfg_if! {
use actix_files::{Files};
use actix_web::*;
use crate::todo::*;
use std::{net::SocketAddr, env};
#[get("/style.css")]
async fn css() -> impl Responder {
@ -17,6 +18,7 @@ cfg_if! {
#[actix_web::main]
async fn main() -> std::io::Result<()> {
let addr = SocketAddr::from(([127,0,0,1],3000));
let mut conn = db().await.expect("couldn't connect to DB");
sqlx::migrate!()
.run(&mut conn)
@ -25,15 +27,23 @@ cfg_if! {
crate::todo::register_server_functions();
HttpServer::new(|| {
HttpServer::new(move || {
let render_options: RenderOptions = RenderOptions::builder()
.pkg_path("/pkg/todo_app_sqlite")
.reload_port(3001)
.socket_address(addr.clone())
.environment(&env::var("RUST_ENV"))
.build();
render_options.write_to_file();
App::new()
.service(Files::new("/pkg", "./pkg"))
.service(css)
.route("/api/{tail:.*}", leptos_actix::handle_server_fns())
.route("/{tail:.*}", leptos_actix::render_app_to_stream("todo_app_sqlite", |cx| view! { cx, <TodoApp/> }))
.route("/{tail:.*}", leptos_actix::render_app_to_stream(render_options, |cx| view! { cx, <TodoApp/> }))
//.wrap(middleware::Compress::default())
})
.bind(("127.0.0.1", 8081))?
.bind(addr)?
.run()
.await
}

View File

@ -88,30 +88,30 @@ pub async fn delete_todo(id: u16) -> Result<(), ServerFnError> {
}
#[component]
pub fn TodoApp(cx: Scope) -> Element {
pub fn TodoApp(cx: Scope) -> impl IntoView {
view! {
cx,
<div>
<Stylesheet href="/style.css"/>
<Router>
<header>
<h1>"My Tasks"</h1>
</header>
<main>
<Routes>
<Route path="" element=|cx| view! {
cx,
<Todos/>
}/>
</Routes>
</main>
</Router>
<Stylesheet href="/style.css"/>
<Router>
<header>
<h1>"My Tasks"</h1>
</header>
<main>
<Routes>
<Route path="" element=|cx| view! {
cx,
<Todos/>
}/>
</Routes>
</main>
</Router>
</div>
}
}
#[component]
pub fn Todos(cx: Scope) -> Element {
pub fn Todos(cx: Scope) -> impl IntoView {
let add_todo = create_server_multi_action::<AddTodo>(cx);
let delete_todo = create_server_action::<DeleteTodo>(cx);
let submissions = add_todo.submissions();
@ -137,78 +137,77 @@ pub fn Todos(cx: Scope) -> Element {
</label>
<input type="submit" value="Add"/>
</MultiActionForm>
<div>
<Suspense fallback=view! {cx, <p>"Loading..."</p> }>
{
<Suspense fallback=move || view! {cx, <p>"Loading..."</p> }>
{
let delete_todo = delete_todo.clone();
move || {
let existing_todos = {
let delete_todo = delete_todo.clone();
move || {
let existing_todos = {
let delete_todo = delete_todo.clone();
move || {
todos
.read()
.map({
let delete_todo = delete_todo.clone();
move |todos| match todos {
Err(e) => {
vec![view! { cx, <pre class="error">"Server Error: " {e.to_string()}</pre>}]
}
Ok(todos) => {
if todos.is_empty() {
vec![view! { cx, <p>"No tasks were found."</p> }]
} else {
todos
.into_iter()
.map({
todos
.read()
.map({
let delete_todo = delete_todo.clone();
move |todos| match todos {
Err(e) => {
vec![view! { cx, <pre class="error">"Server Error: " {e.to_string()}</pre>}.into_any()]
}
Ok(todos) => {
if todos.is_empty() {
vec![view! { cx, <p>"No tasks were found."</p> }.into_any()]
} else {
todos
.into_iter()
.map({
let delete_todo = delete_todo.clone();
move |todo| {
let delete_todo = delete_todo.clone();
move |todo| {
let delete_todo = delete_todo.clone();
view! {
cx,
<li>
{todo.title}
<ActionForm action=delete_todo.clone()>
<input type="hidden" name="id" value={todo.id}/>
<input type="submit" value="X"/>
</ActionForm>
</li>
}
view! {
cx,
<li>
{todo.title}
<ActionForm action=delete_todo.clone()>
<input type="hidden" name="id" value={todo.id}/>
<input type="submit" value="X"/>
</ActionForm>
</li>
}
})
.collect::<Vec<_>>()
}
.into_any()
}
})
.collect::<Vec<_>>()
}
}
})
.unwrap_or_default()
}
};
let pending_todos = move || {
submissions
.get()
.into_iter()
.filter(|submission| submission.pending().get())
.map(|submission| {
view! {
cx,
<li class="pending">{move || submission.input.get().map(|data| data.title) }</li>
}
})
.collect::<Vec<_>>()
};
view! {
cx,
<ul>
<div>{existing_todos}</div>
<div>{pending_todos}</div>
</ul>
.unwrap_or_default()
}
};
let pending_todos = move || {
submissions
.get()
.into_iter()
.filter(|submission| submission.pending().get())
.map(|submission| {
view! {
cx,
<li class="pending">{move || submission.input.get().map(|data| data.title) }</li>
}
})
.collect::<Vec<_>>()
};
view! {
cx,
<ul>
{existing_todos}
{pending_todos}
</ul>
}
}
</Suspense>
</div>
}
</Suspense>
</div>
}
}