mirror of
https://github.com/chenasraf/leptos.git
synced 2026-05-17 17:48:10 +00:00
This is a manual implementation of "server functions." I'm going to use this to build out the RPC protocol that will allow us to do proper, fine-grained data loading and mutations.
This commit is contained in:
@@ -13,6 +13,9 @@ members = [
|
||||
|
||||
# examples
|
||||
"examples/counter",
|
||||
"examples/counter-isomorphic/client",
|
||||
"examples/counter-isomorphic/server",
|
||||
"examples/counter-isomorphic/counter",
|
||||
"examples/counters",
|
||||
"examples/counters-stable",
|
||||
"examples/fetch",
|
||||
@@ -23,7 +26,7 @@ members = [
|
||||
"examples/router",
|
||||
"examples/todomvc",
|
||||
"examples/todomvc-ssr/todomvc-ssr-client",
|
||||
"examples/todomvc-ssr/todomvc-ssr-server"
|
||||
"examples/todomvc-ssr/todomvc-ssr-server",
|
||||
]
|
||||
exclude = [
|
||||
"benchmarks"
|
||||
|
||||
16
examples/counter-isomorphic/client/Cargo.toml
Normal file
16
examples/counter-isomorphic/client/Cargo.toml
Normal file
@@ -0,0 +1,16 @@
|
||||
[package]
|
||||
name = "counter-client"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[lib]
|
||||
crate-type = ["cdylib", "rlib"]
|
||||
|
||||
[dependencies]
|
||||
console_log = "0.2"
|
||||
leptos = { path = "../../../leptos", default-features = false, features = ["hydrate", "serde"] }
|
||||
counter-isomorphic = { path = "../counter", default-features = false, features = ["hydrate"] }
|
||||
log = "0.4"
|
||||
wasm-bindgen = "0.2"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
|
||||
1
examples/counter-isomorphic/client/build.sh
Executable file
1
examples/counter-isomorphic/client/build.sh
Executable file
@@ -0,0 +1 @@
|
||||
wasm-pack build --target=web --release
|
||||
14
examples/counter-isomorphic/client/src/lib.rs
Normal file
14
examples/counter-isomorphic/client/src/lib.rs
Normal file
@@ -0,0 +1,14 @@
|
||||
use counter_isomorphic::*;
|
||||
use leptos::*;
|
||||
use wasm_bindgen::prelude::wasm_bindgen;
|
||||
|
||||
#[wasm_bindgen]
|
||||
pub fn main() {
|
||||
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| {
|
||||
view! { cx, <Counter/> }
|
||||
});
|
||||
}
|
||||
23
examples/counter-isomorphic/counter/Cargo.toml
Normal file
23
examples/counter-isomorphic/counter/Cargo.toml
Normal file
@@ -0,0 +1,23 @@
|
||||
[package]
|
||||
name = "counter-isomorphic"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
leptos = { path = "../../../leptos", default-features = false, features = ["serde"] }
|
||||
leptos_router = { path = "../../../router", default-features = false }
|
||||
console_log = "0.2"
|
||||
gloo-net = "0.2"
|
||||
log = "0.4"
|
||||
console_error_panic_hook = "0.1.7"
|
||||
thiserror = "1"
|
||||
serde = { version = "1", features = ["derive"] }
|
||||
|
||||
[dev-dependencies]
|
||||
wasm-bindgen-test = "0.3.0"
|
||||
|
||||
[features]
|
||||
default = ["csr"]
|
||||
csr = ["leptos/csr", "leptos_router/csr"]
|
||||
hydrate = ["leptos/hydrate", "leptos_router/hydrate"]
|
||||
ssr = ["leptos/ssr", "leptos_router/ssr"]
|
||||
7
examples/counter-isomorphic/counter/index.html
Normal file
7
examples/counter-isomorphic/counter/index.html
Normal file
@@ -0,0 +1,7 @@
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<link data-trunk rel="rust" data-wasm-opt="z"/>
|
||||
</head>
|
||||
<body></body>
|
||||
</html>
|
||||
137
examples/counter-isomorphic/counter/src/lib.rs
Normal file
137
examples/counter-isomorphic/counter/src/lib.rs
Normal file
@@ -0,0 +1,137 @@
|
||||
use std::sync::atomic::{AtomicI32, Ordering};
|
||||
|
||||
use leptos::*;
|
||||
|
||||
use serde::{Deserialize, Serialize};
|
||||
use thiserror::Error;
|
||||
|
||||
#[derive(Error, Debug, Clone, Serialize, Deserialize)]
|
||||
pub enum ServerFnError {
|
||||
#[error("error reaching server to call server function: {0}")]
|
||||
Request(String),
|
||||
#[error("error running server function: {0}")]
|
||||
ServerError(String),
|
||||
#[error("error deserializing server function results {0}")]
|
||||
Deserialization(String),
|
||||
}
|
||||
|
||||
pub async fn call_server_fn<T>(url: &str) -> Result<T, ServerFnError>
|
||||
where
|
||||
T: Serializable + Sized,
|
||||
{
|
||||
let window = window();
|
||||
let resp = gloo_net::http::Request::get(url)
|
||||
.send()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::Request(e.to_string()))?;
|
||||
|
||||
// check for error status
|
||||
let status = resp.status();
|
||||
if status >= 500 && status <= 599 {
|
||||
return Err(ServerFnError::ServerError(resp.status_text()));
|
||||
}
|
||||
|
||||
let text = resp
|
||||
.text()
|
||||
.await
|
||||
.map_err(|e| ServerFnError::Deserialization(e.to_string()))?;
|
||||
T::from_json(&text).map_err(|e| ServerFnError::Deserialization(e.to_string()))
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
static COUNT: AtomicI32 = AtomicI32::new(0);
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn get_server_count() -> Result<i32, ServerFnError> {
|
||||
Ok(COUNT.load(Ordering::Relaxed))
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub async fn get_server_count() -> Result<i32, ServerFnError> {
|
||||
call_server_fn("/api/get_server_count").await
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn increment_server_count() -> Result<i32, ServerFnError> {
|
||||
let new = COUNT.load(Ordering::Relaxed) + 1;
|
||||
COUNT.store(new, Ordering::Relaxed);
|
||||
Ok(new)
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub async fn increment_server_count() -> Result<i32, ServerFnError> {
|
||||
call_server_fn("/api/increment_server_count").await
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn decrement_server_count() -> Result<i32, ServerFnError> {
|
||||
let new = COUNT.load(Ordering::Relaxed) - 1;
|
||||
COUNT.store(new, Ordering::Relaxed);
|
||||
Ok(new)
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub async fn decrement_server_count() -> Result<i32, ServerFnError> {
|
||||
call_server_fn("/api/decrement_server_count").await
|
||||
}
|
||||
|
||||
#[cfg(feature = "ssr")]
|
||||
pub async fn clear_server_count() -> Result<i32, ServerFnError> {
|
||||
COUNT.store(0, Ordering::Relaxed);
|
||||
Ok(0)
|
||||
}
|
||||
#[cfg(not(feature = "ssr"))]
|
||||
pub async fn clear_server_count() -> Result<i32, ServerFnError> {
|
||||
call_server_fn("/api/clear_server_count").await
|
||||
}
|
||||
|
||||
#[component]
|
||||
pub fn Counter(cx: Scope) -> Element {
|
||||
let (update, set_update) = create_signal(cx, 0);
|
||||
let counter = create_resource(cx, move || update(), |_| get_server_count());
|
||||
|
||||
let dec = move |_| {
|
||||
spawn_local(async move {
|
||||
let new_count = decrement_server_count().await;
|
||||
if let Ok(new_count) = new_count {
|
||||
set_update.update(|n| *n += 1);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let inc = move |_| {
|
||||
spawn_local(async move {
|
||||
let new_count = increment_server_count().await;
|
||||
if let Ok(new_count) = new_count {
|
||||
set_update.update(|n| *n += 1);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let clear = move |_| {
|
||||
spawn_local(async move {
|
||||
let new_count = clear_server_count().await;
|
||||
if let Ok(new_count) = new_count {
|
||||
set_update.update(|n| *n += 1);
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
let value = move || counter.read().map(|count| count.unwrap_or(0)).unwrap_or(0);
|
||||
let error_msg = move || {
|
||||
counter
|
||||
.read()
|
||||
.map(|res| match res {
|
||||
Ok(_) => None,
|
||||
Err(e) => Some(e),
|
||||
})
|
||||
.flatten()
|
||||
};
|
||||
|
||||
view! {
|
||||
cx,
|
||||
<div>
|
||||
<button on:click=clear>"Clear"</button>
|
||||
<button on:click=dec>"-1"</button>
|
||||
<span>"Value: " {move || value().to_string()} "!"</span>
|
||||
<button on:click=inc>"+1"</button>
|
||||
</div>
|
||||
}
|
||||
}
|
||||
8
examples/counter-isomorphic/counter/src/main.rs
Normal file
8
examples/counter-isomorphic/counter/src/main.rs
Normal file
@@ -0,0 +1,8 @@
|
||||
pub use counter::*;
|
||||
use leptos::*;
|
||||
|
||||
pub fn main() {
|
||||
_ = console_log::init_with_level(log::Level::Debug);
|
||||
console_error_panic_hook::set_once();
|
||||
mount_to_body(|cx| view! { cx, <Counter/> });
|
||||
}
|
||||
10
examples/counter-isomorphic/server/Cargo.toml
Normal file
10
examples/counter-isomorphic/server/Cargo.toml
Normal file
@@ -0,0 +1,10 @@
|
||||
[package]
|
||||
name = "counter-server"
|
||||
version = "0.1.0"
|
||||
edition = "2021"
|
||||
|
||||
[dependencies]
|
||||
actix-files = "0.6"
|
||||
actix-web = "4"
|
||||
leptos = { path = "../../../leptos", default-features = false, features = ["ssr", "serde"] }
|
||||
counter-isomorphic = { path = "../counter", default-features = false, features = ["ssr"] }
|
||||
76
examples/counter-isomorphic/server/src/main.rs
Normal file
76
examples/counter-isomorphic/server/src/main.rs
Normal file
@@ -0,0 +1,76 @@
|
||||
use actix_files::Files;
|
||||
use actix_web::*;
|
||||
use counter_isomorphic::*;
|
||||
use leptos::*;
|
||||
|
||||
#[get("/")]
|
||||
async fn render_todomvc() -> impl Responder {
|
||||
HttpResponse::Ok().content_type("text/html").body(format!(
|
||||
r#"<!DOCTYPE html>
|
||||
<html lang="en">
|
||||
<head>
|
||||
<meta charset="utf-8"/>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1"/>
|
||||
<title>Isomorphic Counter</title>
|
||||
</head>
|
||||
<body>
|
||||
{}
|
||||
</body>
|
||||
<script type="module">import init, {{ main }} from './pkg/counter_client.js'; init().then(main);</script>
|
||||
</html>"#,
|
||||
run_scope({
|
||||
|cx| {
|
||||
view! { cx, <Counter/>}
|
||||
}
|
||||
})
|
||||
))
|
||||
}
|
||||
|
||||
#[get("/api/get_server_count")]
|
||||
async fn get_server_count() -> impl Responder {
|
||||
counter_isomorphic::get_server_count()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[get("/api/clear_server_count")]
|
||||
async fn clear_server_count() -> impl Responder {
|
||||
counter_isomorphic::clear_server_count()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[get("/api/increment_server_count")]
|
||||
async fn increment_server_count() -> impl Responder {
|
||||
counter_isomorphic::increment_server_count()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[get("/api/decrement_server_count")]
|
||||
async fn decrement_server_count() -> impl Responder {
|
||||
counter_isomorphic::decrement_server_count()
|
||||
.await
|
||||
.unwrap()
|
||||
.to_string()
|
||||
}
|
||||
|
||||
#[actix_web::main]
|
||||
async fn main() -> std::io::Result<()> {
|
||||
HttpServer::new(|| {
|
||||
App::new()
|
||||
.service(render_todomvc)
|
||||
.service(Files::new("/pkg", "../client/pkg"))
|
||||
.service(get_server_count)
|
||||
.service(clear_server_count)
|
||||
.service(increment_server_count)
|
||||
.service(decrement_server_count)
|
||||
.wrap(middleware::Compress::default())
|
||||
})
|
||||
.bind(("127.0.0.1", 8080))?
|
||||
.run()
|
||||
.await
|
||||
}
|
||||
Reference in New Issue
Block a user