Skip to content

Commit

Permalink
Split route & components
Browse files Browse the repository at this point in the history
  • Loading branch information
iwanbazz committed Aug 28, 2020
1 parent f567b22 commit e02c81f
Show file tree
Hide file tree
Showing 18 changed files with 388 additions and 87 deletions.
5 changes: 5 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,4 +1,9 @@
.DS_Store
/target
Cargo.lock
static/*.wasm
static/*.d.ts
static/wasm.js
static/package.json

http://www.sheshbabu.com/posts/rust-wasm-yew-single-page-application/
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ crate-type = ["cdylib","rlib"]

[dependencies]
yew = "0.17"
yew-router = "0.14.0"
wasm-bindgen = "0.2"
anyhow = "1.0.32"
serde = { version = "1.0", features = ["derive"] }
2 changes: 1 addition & 1 deletion Makefile.toml
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
[tasks.build]
command = "wasm-pack"
args = ["build", "--dev", "--target", "web", "--out-name", "wasm", "--out-dir", "./static"]
watch = {ignore_pattern = "static/*"}
watch = { ignore_pattern = "static/*" }

[tasks.serve]
command = "simple-http-server"
Expand Down
8 changes: 8 additions & 0 deletions src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,11 @@ pub fn get_products(callback: FetchCallback<Vec<Product>>) -> FetchTask {

FetchService::fetch(req, callback).unwrap()
}

pub fn get_product(id: i32, callback: FetchCallback<Product>) -> FetchTask {
let req = Request::get(format!("/products/{}.json", id))
.body(Nothing)
.unwrap();

FetchService::fetch(req, callback).unwrap()
}
83 changes: 83 additions & 0 deletions src/app.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
use crate::components::Navbar;
use crate::types::{CartProduct, Product};
use yew::prelude::*;
use yew_router::prelude::*;

use crate::pages::{Home, ProductDetail};
use crate::route::Route;

struct State {
cart_products: Vec<CartProduct>,
}

pub struct App {
state: State,
link: ComponentLink<Self>,
}

pub enum Msg {
AddToCart(Product),
}

impl Component for App {
type Message = Msg;
type Properties = ();

fn create(_: Self::Properties, link: ComponentLink<Self>) -> Self {
let cart_products = vec![];

Self {
state: State { cart_products },
link,
}
}

fn update(&mut self, message: Self::Message) -> ShouldRender {
match message {
Msg::AddToCart(product) => {
let cart_product = self
.state
.cart_products
.iter_mut()
.find(|cp: &&mut CartProduct| cp.product.id == product.id);

if let Some(cp) = cart_product {
cp.quantity += 1;
} else {
self.state.cart_products.push(CartProduct {
product: product.clone(),
quantity: 1,
})
}
true
}
}
}

fn change(&mut self, _: Self::Properties) -> ShouldRender {
false
}

fn view(&self) -> Html {
let handle_add_to_cart = self
.link
.callback(|product: Product| Msg::AddToCart(product));
let cart_products = self.state.cart_products.clone();

let render = Router::render(move |switch: Route| match switch {
Route::ProductDetail(id) => {
html! {<ProductDetail id=id on_add_to_cart=handle_add_to_cart.clone() />}
}
Route::HomePage => {
html! {<Home cart_products=cart_products.clone() on_add_to_cart=handle_add_to_cart.clone()/>}
}
});

html! {
<>
<Navbar cart_products=self.state.cart_products.clone() />
<Router<Route, ()> render=render/>
</>
}
}
}
46 changes: 46 additions & 0 deletions src/components/atc_button.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
use crate::types::Product;
use yew::prelude::*;

pub struct AtcButton {
props: Props,
link: ComponentLink<Self>,
}

#[derive(Properties, Clone)]
pub struct Props {
pub product: Product,
pub on_add_to_cart: Callback<Product>,
}

pub enum Msg {
AddToCart,
}

impl Component for AtcButton {
type Message = Msg;
type Properties = Props;

fn create(props: Self::Properties, link: ComponentLink<Self>) -> Self {
Self { props, link }
}

fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::AddToCart => self.props.on_add_to_cart.emit(self.props.product.clone()),
}
true
}

fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props = props;
true
}

fn view(&self) -> Html {
let onclick = self.link.callback(|_| Msg::AddToCart);

html! {
<button class="product_atc_button" onclick=onclick>{"Add To Cart"}</button>
}
}
}
4 changes: 4 additions & 0 deletions src/components/mod.rs
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
mod atc_button;
mod navbar;
mod product_card;

pub use atc_button::AtcButton;
pub use navbar::Navbar;
pub use product_card::ProductCard;
44 changes: 44 additions & 0 deletions src/components/navbar.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
use crate::types::CartProduct;
use yew::prelude::*;

pub struct Navbar {
props: Props,
}

#[derive(Properties, Clone)]
pub struct Props {
pub cart_products: Vec<CartProduct>,
}

impl Component for Navbar {
type Message = ();
type Properties = Props;

fn create(props: Self::Properties, _link: ComponentLink<Self>) -> Self {
Self { props }
}

fn update(&mut self, _msg: Self::Message) -> ShouldRender {
true
}

fn change(&mut self, props: Self::Properties) -> ShouldRender {
self.props = props;
true
}

fn view(&self) -> Html {
let cart_value = self
.props
.cart_products
.iter()
.fold(0.0, |acc, cp| acc + (cp.quantity as f64 * cp.product.price));

html! {
<div class="navbar">
<div class="navbar_title">{"RustMart"}</div>
<div class="navbar_cart_value">{format!("${:.2}", cart_value)}</div>
</div>
}
}
}
21 changes: 13 additions & 8 deletions src/components/product_card.rs
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
use crate::components::AtcButton;
use crate::route::Route;
use crate::types::Product;
use yew::prelude::*;
use yew_router::components::RouterAnchor;

pub struct ProductCard {
props: Props,
Expand All @@ -8,7 +11,7 @@ pub struct ProductCard {
#[derive(Properties, Clone)]
pub struct Props {
pub product: Product,
pub on_add_to_cart: Callback<()>,
pub on_add_to_cart: Callback<Product>,
}

impl Component for ProductCard {
Expand All @@ -28,15 +31,17 @@ impl Component for ProductCard {
}

fn view(&self) -> Html {
let onclick = self.props.on_add_to_cart.reform(|_| ());
type Anchor = RouterAnchor<Route>;

html! {
<div>
<img src={&self.props.product.image}/>
<div>{&self.props.product.name}</div>
<div>{"$"}{&self.props.product.price}</div>
<button onclick=onclick>{"Add To Cart"}</button>
</div>
<div class="product_card_container">
<Anchor route=Route::ProductDetail(self.props.product.id) classes="product_card_anchor">
<img class="product_card_image" src={&self.props.product.image}/>
<div class="product_card_name">{&self.props.product.name}</div>
<div class="product_card_price">{"$"}{&self.props.product.price}</div>
</Anchor>
<AtcButton product=self.props.product.clone() on_add_to_cart=self.props.on_add_to_cart.clone() />
</div>
}
}
}
7 changes: 4 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,13 +1,14 @@
mod api;
mod app;
mod components;
mod types;
mod pages;
mod route;
mod types;

use pages::Home;
use wasm_bindgen::prelude::*;
use yew::prelude::*;

#[wasm_bindgen(start)]
pub fn run_app() {
App::<Home>::new().mount_to_body();
App::<app::App>::new().mount_to_body();
}
Loading

0 comments on commit e02c81f

Please sign in to comment.