Create and Edit the Page
Explore how to set up and implement page creation and editing in Rust using the Yew framework. Learn to manage forms, send API requests with fetch, and apply CSS frameworks like Bootstrap to enhance your front-end design.
We'll cover the following...
Initial setup
We need to create our form to add new products and edit them. But first, we need a few dependencies in our project. Add the next lines to the Cargo.toml file.
We want our front-end to look good, so we might add a CSS framework. We can use bootstrap for that. Add the next line to the index.html file.
Next, we add a few structs to handle the parameters needed to send through the API to our backend server. Remember that the create endpoint needs a NewCompleteProduct, so we need to replicate the same in our front-end.
We also need to create the Ajax request. We can use the fetch API provided from our browser, which is also available as a service in Yew.
The first parameter is product with all the data needed, and the second one is callback that handles our response.
Component
use yew::callback::Callback;
use yew::services::fetch::{FetchService, FetchTask, Request, Response};
use yew::format::Json;
use serde_json::json;
use serde::Serialize;
use anyhow::Error;
use yew::prelude::{html, InputData, classes, Component, ComponentLink, Html, ShouldRender};
// An enum to list all the available actions
enum Msg {
UpdateName(String),
UpdateCost(f64),
UpdateActive(bool),
ReceiveResponse(Result<String, anyhow::Error>),
Submit
}
// The model is where we save our state
struct Model {
link: ComponentLink<Self>,
product: FormCompleteProduct,
fetch_task: Option<FetchTask>,
result_fetch: String
}
// We need to implement Component to have all the functionalities available
impl Component for Model {
type Message = Msg;
type Properties = ();
fn create(_props: Self::Properties, link: ComponentLink<Self>) -> Self {
Self {
link,
product: FormCompleteProduct::default(),
fetch_task: None,
result_fetch: "".to_string()
}
}
fn update(&mut self, msg: Self::Message) -> ShouldRender {
match msg {
Msg::Submit => {
let callback =
self.link
.callback(|response: Response<Json<Result<String, anyhow::Error>>>| {
let Json(data) = response.into_body();
Msg::ReceiveResponse(data)
});
self.fetch_task = Some(create_product(self.product.clone(), callback));
},
Msg::UpdateActive(active) => self.product.product.active = active,
Msg::UpdateCost(cost) => self.product.product.cost = cost,
Msg::UpdateName(name) => self.product.product.name = name,
Msg::ReceiveResponse(result) => {
match result {
Ok(ok_result) => self.result_fetch = ok_result,
Err(error) => self.result_fetch = error.to_string()
}
}
}
true
}
fn change(&mut self, _props: Self::Properties) -> ShouldRender {
false
}
// Our presentation layer
fn view(&self) -> Html {
html! {
<div class={classes!("container")}>
<div class={classes!("card", "mb-3", "row", "col-lg-3")}>
<h5 class={classes!("card-header")}>
{ "Product" }
</h5>
<div class={classes!("card-body")}>
<form>
<label class={classes!("form-label")} for="product-name">
{ "Name" }
</label>
<input
id="product-name"
class={classes!("form-control")}
placeholder="Name"
value={self.product.product.name.clone()}
oninput={self.link.callback(|e: InputData| Msg::UpdateName(e.value))}
/>
<label class={classes!("form-label")} for="product-cost">
{ "Cost" }
</label>
<input
id="product-cost"
class={classes!("form-control")}
placeholder="Cost"
value={self.product.product.cost.to_string()}
oninput={self.link.callback(|e: InputData| {
let cost = e.value.parse::<f64>().expect("Not a number");
Msg::UpdateCost(cost)
})}
/>
<div class={classes!("form-check")}>
<input
id="product-active"
class={classes!("form-check-input")}
type="checkbox"
checked={self.product.product.active}
oninput={self.link.callback(|e: InputData| {
let active = e.value.parse::<bool>().expect("Not a boolean");
Msg::UpdateActive(active)
})}
/>
<label class={classes!("form-check-label")} for="product-active">
{ "Active" }
</label>
</div>
<button
class={classes!("btn", "btn-primary")}
onclick=self.link.callback(|_| Msg::Submit)>{ "Save" }
</button>
</form>
</div>
</div>
</div>
}
}
}
fn main() {
yew::start_app::<Model>();
}
We use the struct Msg to capture the action from the view. The Model struct is our component that’ll have the update function, which captures the defined interaction from the view function.