mirror of
https://github.com/leptos-rs/leptos
synced 2024-11-10 14:54:16 +00:00
Router example
This commit is contained in:
parent
d6c007dcc4
commit
89d30d017c
3 changed files with 140 additions and 50 deletions
|
@ -2,6 +2,17 @@
|
||||||
<html>
|
<html>
|
||||||
<head>
|
<head>
|
||||||
<link data-trunk rel="rust" data-wasm-opt="z"/>
|
<link data-trunk rel="rust" data-wasm-opt="z"/>
|
||||||
|
<style>
|
||||||
|
a.active {
|
||||||
|
font-weight: bold;
|
||||||
|
}
|
||||||
|
|
||||||
|
.contact, .contact-list {
|
||||||
|
border: 1px solid #c0c0c0;
|
||||||
|
border-radius: 3px;
|
||||||
|
padding: 1rem;
|
||||||
|
}
|
||||||
|
</style>
|
||||||
</head>
|
</head>
|
||||||
<body></body>
|
<body></body>
|
||||||
</html>
|
</html>
|
|
@ -29,17 +29,29 @@ pub struct Contact {
|
||||||
|
|
||||||
pub async fn get_contacts(search: String) -> Vec<ContactSummary> {
|
pub async fn get_contacts(search: String) -> Vec<ContactSummary> {
|
||||||
// fake an API call with an artificial delay
|
// fake an API call with an artificial delay
|
||||||
delay(Duration::from_millis(100)).await;
|
delay(Duration::from_millis(300)).await;
|
||||||
vec![ContactSummary {
|
vec![
|
||||||
id: 0,
|
ContactSummary {
|
||||||
first_name: "Bill".into(),
|
id: 0,
|
||||||
last_name: "Smith".into(),
|
first_name: "Bill".into(),
|
||||||
}]
|
last_name: "Smith".into(),
|
||||||
|
},
|
||||||
|
ContactSummary {
|
||||||
|
id: 1,
|
||||||
|
first_name: "Tim".into(),
|
||||||
|
last_name: "Jones".into(),
|
||||||
|
},
|
||||||
|
ContactSummary {
|
||||||
|
id: 2,
|
||||||
|
first_name: "Sally".into(),
|
||||||
|
last_name: "Stevens".into(),
|
||||||
|
},
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
||||||
pub async fn get_contact(id: Option<usize>) -> Option<Contact> {
|
pub async fn get_contact(id: Option<usize>) -> Option<Contact> {
|
||||||
// fake an API call with an artificial delay
|
// fake an API call with an artificial delay
|
||||||
delay(Duration::from_millis(350)).await;
|
delay(Duration::from_millis(500)).await;
|
||||||
match id {
|
match id {
|
||||||
Some(0) => Some(Contact {
|
Some(0) => Some(Contact {
|
||||||
id: 0,
|
id: 0,
|
||||||
|
@ -53,6 +65,30 @@ pub async fn get_contact(id: Option<usize>) -> Option<Contact> {
|
||||||
email: "bill@smith.com".into(),
|
email: "bill@smith.com".into(),
|
||||||
phone: "617-121-1221".into(),
|
phone: "617-121-1221".into(),
|
||||||
}),
|
}),
|
||||||
|
Some(1) => Some(Contact {
|
||||||
|
id: 1,
|
||||||
|
first_name: "Tim".into(),
|
||||||
|
last_name: "Jones".into(),
|
||||||
|
address_1: "56 Main Street".into(),
|
||||||
|
address_2: "".into(),
|
||||||
|
city: "Chattanooga".into(),
|
||||||
|
state: "TN".into(),
|
||||||
|
zip: "13371".into(),
|
||||||
|
email: "timjones@lmail.com".into(),
|
||||||
|
phone: "232-123-1337".into(),
|
||||||
|
}),
|
||||||
|
Some(2) => Some(Contact {
|
||||||
|
id: 2,
|
||||||
|
first_name: "Sally".into(),
|
||||||
|
last_name: "Stevens".into(),
|
||||||
|
address_1: "404 E 123rd St".into(),
|
||||||
|
address_2: "Apt 7E".into(),
|
||||||
|
city: "New York".into(),
|
||||||
|
state: "NY".into(),
|
||||||
|
zip: "10082".into(),
|
||||||
|
email: "sally.stevens@wahoo.net".into(),
|
||||||
|
phone: "242-121-3789".into(),
|
||||||
|
}),
|
||||||
_ => None,
|
_ => None,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -13,105 +13,148 @@ use crate::api::{get_contact, get_contacts};
|
||||||
|
|
||||||
fn contact_list(
|
fn contact_list(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
params: ParamsMap,
|
params: Memo<ParamsMap>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) -> Resource<String, Vec<ContactSummary>> {
|
) -> Resource<String, Vec<ContactSummary>> {
|
||||||
log::debug!("(contact_list) reloading contact list");
|
log::debug!("(contact_list) reloading contact list");
|
||||||
create_resource(cx, location.search, move |s| get_contacts(s.to_string()))
|
create_resource(cx, location.search, get_contacts)
|
||||||
}
|
}
|
||||||
|
|
||||||
fn contact(
|
fn contact(
|
||||||
cx: Scope,
|
cx: Scope,
|
||||||
params: ParamsMap,
|
params: Memo<ParamsMap>,
|
||||||
location: Location,
|
location: Location,
|
||||||
) -> Resource<Option<usize>, Option<Contact>> {
|
) -> Resource<Option<usize>, Option<Contact>> {
|
||||||
log::debug!("(contact) reloading contact");
|
log::debug!("(contact) reloading contact");
|
||||||
create_resource(
|
create_resource(
|
||||||
cx,
|
cx,
|
||||||
move || {
|
move || {
|
||||||
params
|
params()
|
||||||
.get("id")
|
.get("id")
|
||||||
.cloned()
|
.cloned()
|
||||||
.unwrap_or_default()
|
.unwrap_or_default()
|
||||||
.parse::<usize>()
|
.parse::<usize>()
|
||||||
.ok()
|
.ok()
|
||||||
},
|
},
|
||||||
move |id| get_contact(id),
|
get_contact,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn router_example(cx: Scope) -> Element {
|
pub fn router_example(cx: Scope) -> Element {
|
||||||
view! {
|
view! {
|
||||||
<div>
|
<div>
|
||||||
<nav>
|
<Router
|
||||||
<a href="/">"Contacts"</a>
|
mode=BrowserIntegration {}
|
||||||
<a href="/about">"About"</a>
|
>
|
||||||
<a href="/settings">"Settings"</a>
|
<Routes>
|
||||||
</nav>
|
<Route path=""
|
||||||
<main>
|
element=move |cx| view! { <Index/> }
|
||||||
<Router
|
>
|
||||||
mode=BrowserIntegration {}
|
|
||||||
base="/"
|
|
||||||
>
|
|
||||||
<Routes>
|
|
||||||
<Route
|
<Route
|
||||||
path=""
|
path="contacts"
|
||||||
element=move || view! { <ContactList/> }
|
element=move |cx| view! { <ContactList/> }
|
||||||
loader=contact_list.into()
|
loader=contact_list.into()
|
||||||
>
|
>
|
||||||
<Route
|
<Route
|
||||||
path=":id"
|
path=":id"
|
||||||
loader=contact.into()
|
loader=contact.into()
|
||||||
element=move || view! { <Contact/> }
|
element=move |cx| view! { <Contact/> }
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path="about"
|
||||||
|
loader=contact.into()
|
||||||
|
element=move |cx| view! { <p class="contact">"Here is your list of contacts"</p> }
|
||||||
|
/>
|
||||||
|
<Route
|
||||||
|
path=""
|
||||||
|
loader=contact.into()
|
||||||
|
element=move |cx| view! { <p class="contact">"Select a contact."</p> }
|
||||||
/>
|
/>
|
||||||
</Route>
|
</Route>
|
||||||
<Route
|
<Route
|
||||||
path="about"
|
path="about"
|
||||||
element=move || view! { <About/> }
|
element=move |cx| view! { <About/> }
|
||||||
/>
|
/>
|
||||||
<Route
|
<Route
|
||||||
path="settings"
|
path="settings"
|
||||||
element=move || view! { <Settings/> }
|
element=move |cx| view! { <Settings/> }
|
||||||
/>
|
/>
|
||||||
</Routes>
|
</Route>
|
||||||
</Router>
|
</Routes>
|
||||||
</main>
|
</Router>
|
||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn ContactList(cx: Scope) -> Vec<Element> {
|
pub fn Index(cx: Scope) -> Vec<Element> {
|
||||||
let contacts = use_loader::<Resource<String, Vec<ContactSummary>>>(cx);
|
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<>
|
<>
|
||||||
<h1>"Contacts"</h1>
|
<nav>
|
||||||
<ul>
|
<NavLink to="contacts".into()>"Contacts"</NavLink>
|
||||||
<For each={move || contacts.read().unwrap_or_default()} key=|contact| contact.id>
|
<NavLink to="about".into()>"About"</NavLink>
|
||||||
{|cx, contact: &ContactSummary| {
|
<NavLink to="settings".into()>"Settings"</NavLink>
|
||||||
view! {
|
</nav>
|
||||||
<li><a href=format!("/contacts/{}", contact.id)> {&contact.first_name} " " {&contact.last_name}</a></li>
|
<main><Outlet/></main>
|
||||||
}
|
|
||||||
}}
|
|
||||||
</For>
|
|
||||||
</ul>
|
|
||||||
<div><Outlet/></div>
|
|
||||||
</>
|
</>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Contact(cx: Scope) -> Element {
|
pub fn ContactList(cx: Scope) -> Element {
|
||||||
//let contact = use_loader::<Resource<Option<usize>, Option<Contact>>>(cx);
|
let contacts = use_loader::<Resource<String, Vec<ContactSummary>>>(cx);
|
||||||
|
|
||||||
|
log::debug!(
|
||||||
|
"[ContactList] before <Suspense/>, use_route(cx).path() is {:?}",
|
||||||
|
use_route(cx).path()
|
||||||
|
);
|
||||||
|
|
||||||
view! {
|
view! {
|
||||||
<pre>"Contact info here"</pre>
|
<div class="contact-list">
|
||||||
|
<h1>"Contacts"</h1>
|
||||||
|
<Link to="about".into()>"About"</Link>
|
||||||
|
<NavLink to={0.to_string()}>"Link to first contact"</NavLink>
|
||||||
|
<ul>
|
||||||
|
<Suspense fallback=move || view! { <p>"Loading contacts..."</p> }>{
|
||||||
|
move || {
|
||||||
|
log::debug!("[ContactList] inside <Suspense/>, use_route(cx) is now {:?}", use_route(cx).path());
|
||||||
|
view! {
|
||||||
|
<For each={move || contacts.read().unwrap_or_default()} key=|contact| contact.id>
|
||||||
|
{move |cx, contact: &ContactSummary| {
|
||||||
|
view! {
|
||||||
|
<li><NavLink to={contact.id.to_string()}><span>{&contact.first_name} " " {&contact.last_name}</span></NavLink></li>
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
</For>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}</Suspense>
|
||||||
|
</ul>
|
||||||
|
<Outlet/>
|
||||||
|
</div>
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn About(cx: Scope) -> Vec<Element> {
|
pub fn Contact(cx: Scope) -> Element {
|
||||||
|
let contact = use_loader::<Resource<Option<usize>, Option<Contact>>>(cx);
|
||||||
|
|
||||||
|
view! {
|
||||||
|
<div class="contact">
|
||||||
|
<Suspense fallback=move || view! { <p>"Loading..."</p> }>{
|
||||||
|
move || contact.read().flatten().map(|contact| view! {
|
||||||
|
<section class="card">
|
||||||
|
<h1>{contact.first_name} " " {contact.last_name}</h1>
|
||||||
|
<p>{contact.address_1}<br/>{contact.address_2}</p>
|
||||||
|
</section>
|
||||||
|
})
|
||||||
|
}</Suspense>
|
||||||
|
</div>
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[component]
|
||||||
|
pub fn About(_cx: Scope) -> Vec<Element> {
|
||||||
view! {
|
view! {
|
||||||
<>
|
<>
|
||||||
<h1>"About"</h1>
|
<h1>"About"</h1>
|
||||||
|
@ -121,7 +164,7 @@ pub fn About(cx: Scope) -> Vec<Element> {
|
||||||
}
|
}
|
||||||
|
|
||||||
#[component]
|
#[component]
|
||||||
pub fn Settings(cx: Scope) -> Vec<Element> {
|
pub fn Settings(_cx: Scope) -> Vec<Element> {
|
||||||
view! {
|
view! {
|
||||||
<>
|
<>
|
||||||
<h1>"Settings"</h1>
|
<h1>"Settings"</h1>
|
||||||
|
|
Loading…
Reference in a new issue