Props
It’s possible to pass data from the server to the front-end as simple props. To do this, simply
create a HashMap
with &str
keys and InertiaProp
values. Then, use Inertia::render_with_props
passing the props hashmap as the third parameter.
Also, for defining the props hash map, we provide a very trivial macro: hashmap!
. You can use it
like this:
use inertia_rust::{hashmap, Inertia, InertiaFacade, InertiaProp};
use actix_web::{get, web, HttpRequest, Responder};
use serde_json::{json};
use serde::Serialize;
#[derive(Serialize)]
struct User {
pub name: String,
pub email: String,
}
impl User {
// let's just pretend this is a method that fetch users from some database
pub async fn all() -> Vec<User> {
vec![
User {
name: "John Doe".into(),
email: "johndoe@gmail.com".into()
},
User {
name: "Ada Lovelace".into(),
email: "thereWereNoEmailByThatTimeActually!@gmail.com".into(),
}
]
}
}
#[get("/users")]
async fn home(req: HttpRequest) -> impl Responder {
let props = hashmap![
"users" => InertiaProp::data(User::all().await),
];
Inertia::render_with_props(&req, "Users/Index".into(), props).await
}
Indeed, all hashmap!
do is to create a std::collections::HashMap
and insert each item you’ve passed
into it.
Note that a new enum has been introduced to our code right now: InertiaProp
. It contains all the variants
of props that Inertia can handle.
Every InertiaProp
variant must contain — or resolve to — an Result<serde_json::Value, InertiaError
>
object. You can achieve this by calling value.into_inertia_value()
. value
must be serializable. Also,
you must bring the IntoInertiaPropResult
trait into the scope.
If you’re using InertiaProp
helpers, you don’t even need to worry about serializing by yourself (again,
since the object is serializable):
use inertia_rust::{InertiaProp, IntoInertiaPropResult};
let prop = InertiaProp::Data("foo".into_inertia_value());
// or
let prop = InertiaProp::data("foo"); // using `data` helper for generating a `InertiaProp::Data`
On the official Inertia.js documentation, you may find the following PHP snippet:
return Inertia::render('Users/Index', [
// ALWAYS included on standard visits
// OPTIONALLY included on partial reloads
// ALWAYS evaluated
'users' => User::all(),
// ALWAYS included on standard visits
// OPTIONALLY included on partial reloads
// ONLY evaluated when needed
'users' => fn () => User::all(),
// NEVER included on standard visits
// OPTIONALLY included on partial reloads
// ONLY evaluated when needed
'users' => Inertia::lazy(fn () => User::all()),
// ALWAYS included on standard visits
// ALWAYS included on partial reloads
// ALWAYS evaluated
'users' => Inertia::always(User::all()),
]);
You can achieve the exact same behaviors with Inertia Rust:
return Inertia::render_with_props(&req, "Users/Index", hashmap![
// ALWAYS included on standard visits
// OPTIONALLY included on partial reloads
// ALWAYS evaluated
"users" => InertiaProp::data(User::all().await),
// ALWAYS included on standard visits
// OPTIONALLY included on partial reloads
// ONLY evaluated when needed
"users" => InertiaProp::lazy(prop_resolver!({ User::all().await.into_inertia_value() })),
// NEVER included on standard visits
// OPTIONALLY included on partial reloads
// ONLY evaluated when needed
"users" => InertiaProp::demand(prop_resolver!({ User::all().await.into_inertia_value() })),
// ALWAYS included on standard visits
// ALWAYS included on partial reloads
// ALWAYS evaluated
"users" => InertiaProp::always(User::all().await),
]);
The prop_resolver!
macro
Inertia props that take callbacks — such as Lazy
, Demand
and Deferred
variants — are
asynchronous, so that you can .await
inside of them. To use it, it’d be necessary the following code:
use std::sync::Arc;
use inertia_rust::InertiaProp;
use inertia_rust::IntoInertiaPropResult;
let lazy_prop = InertiaProp::Lazy(Arc::new(move || Box::pin(async move {
User::all().await.into_inertia_value();
})));
Such a massive amount of code, we know. However, it’s necessary in order to have asynchronous callbacks support.
In order to avoid writting all this boilerplate, Inertia Rust provide another macro: prop_resolver!
,
as you’ve seen above:
use std::sync::Arc;
use inertia_rust::{prop_resolver, InertiaProp, IntoInertiaPropResult};
let lazy_prop = InertiaProp::Lazy(prop_resolver!({ User::all().await.into_inertia_value() }));
There are some cases — mainly in test environments or playgrounds — where you might want to mock some database and, therefore, need to move values to inside of the resolver closure:
use std::sync::Arc;
use inertia_rust::InertiaProp;
use inertia_rust::IntoInertiaPropResult;
let user = Arc::new(User { name: "John Doe".into(), email: "johndoe@gmail.com".into() });
let permissions = Arc::new(vec!["read", "delete", "update", "delete"]);
InertiaProp::lazy(Arc::new(move || {
let user = user.clone();
let permissions = permissions.clone();
Box::pin(async move {
user_can(user, permissions).await.into_inertia_value()
})
}));
Unfortunately, we have to move a clone of the Arc
-wrapped variables to inside of the closure,
then move again to inside of the inner async closure.
prop_resolver!
also covers this. As first parameter, you’ll pass the “cloning” statements separated
by commas. The actual async closure goes as the second parameter, then:
use std::sync::Arc;
use inertia_rust::{prop_resolver, InertiaProp, IntoInertiaPropResult};
let user = Arc::new(User { name: "John Doe".into(), email: "johndoe@gmail.com".into() });
let permissions = Arc::new(vec!["read", "delete", "update", "delete"]);
InertiaProp::lazy(prop_resolver!(
let user = user.clone(), // statements separated by comma
let permissions = permissions.clone(); // statements and block separated by semicolon
{ user_can(user, permissions).await.into_inertia_value() } // block
));