Router example

This commit is contained in:
Greg Johnston 2022-08-29 08:05:14 -04:00
parent d6c007dcc4
commit 89d30d017c
3 changed files with 140 additions and 50 deletions

View file

@ -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>

View file

@ -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,
} }
} }

View file

@ -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>