How ActionForm handles errors and results in island mode #3642
-
ActionForm is not set to redirect the current page after submission. If an error occurs in the server function, the error will be displayed in the query part of the url. I can't get the return value and error message effectively by using action.value () I now have such a scenario, the existing login form, when the login fails to pop up the dialog and display the reason for the error, if successful, I will call the authentication server function, if the authentication function is successful, I need to jump to other pages, if the authentication fails, I also need to pop up the dialog and display the reason for the error At the same time, my route needs to be protected. If there is no login, I can only enter the login page. Entering other pages will be redirected to the login page. After logging in, the login page will redirect to other pages, but I try to add Store to save the login status. After logging in on the login page, set the field status of Store to true and then return to the protected route. When the field in Store is still false. #[derive(Clone, Debug, Default, Store)]
pub struct GlobalState {
pub logged: bool,
}
#[component]
pub fn App() -> impl IntoView {
provide_meta_context();
let global_state = Store::new(GlobalState { logged: false });
provide_context(global_state);
view! {
<Stylesheet id="leptos" href="/pkg/sinapis.css"/>
// sets the document title
<Title text="Welcome to Leptos"/>
<Router>
<Routes transition=true fallback=|| "Page not found.">
<ParentRoute path=path!("login") view=move || view! {
<Suspense fallback=|| "redirect home">
<Show when=move|| { *global_state.logged().read() }>
<Redirect path="/" />
</Show>
</Suspense>
<Outlet />
}>
<Route path=path!("login") view=Hero />
</ParentRoute>
<ParentRoute path=path!("") view=move || view! {
<Suspense fallback=|| "redirect login">
<Show when=move|| { !*global_state.logged().read() }>
<Redirect path="/login" />
</Show>
</Suspense>
<Outlet />
}>
<Route path=path!("") view=HomePage />
</ParentRoute>
</Routes>
</Router>
}
#[component]
pub fn Hero() -> impl IntoView {
view! {
// <div
// class="hero min-h-screen"
// style="background-image: url(https://img.daisyui.com/images/stock/photo-1507358522600-9f71e620c44e.webp);">
// <div class="hero-overlay bg-opacity-60"></div>
<div class="hero bg-base-200 min-h-screen">
<div class="hero-content flex-col lg:flex-row-reverse">
<div class="text-center lg:text-left">
<h1 class="text-5xl font-bold text-slate-300">{move_tr!("login-now")}</h1>
<p class="py-6 text-slate-400">
Provident cupiditate voluptatem et in. Quaerat fugiat ut assumenda excepturi exercitationem
quasi. In deleniti eaque aut repudiandae et a id nisi.
</p>
</div>
<div class="card bg-base-100 w-full max-w-sm shrink-0 shadow-2xl">
<div class="card-body">
<LoginForm />
</div>
</div>
</div>
</div>
// </div>
}
}
#[component]
fn LoginForm() -> impl IntoView {
let action = ServerAction::<Login>::new();
let query = use_query_map();
let global_state = expect_context::<Store<GlobalState>>();
Effect::new(move |_| {
leptos::logging::log!("{}", query().to_query_string());
});
let accessed = Action::new(|_: &()| async move {
access("/").await
});
let res = Resource::new(
move || action.version()(),
move |_| async move {
if let Some(res) = action.value()() {
match res {
Ok(r) => {
global_state.logged().set(true);
accessed.dispatch(());
}
Err(e) => {
leptos::logging::log!("{}", e);
}
}
}
},
);
let res1 = Resource::new(
move || accessed.version()(),
move |_| async move {
if let Some(res) = accessed.value()() {
match res {
Ok(r) => match r {
true => {
let navigate = use_navigate();
navigate("/", Default::default());
}
false => {
leptos::logging::log!("accessed failed");
}
}
Err(e) => {
leptos::logging::log!("{}", e);
}
}
}
},
);
view! {
<ActionForm action>
<div class="form-control">
<label class="label">
<span class="label-text">{move_tr!("account")}</span>
</label>
<input type="text" name="form[account]" placeholder=move_tr!("account-placeholder") class="input input-bordered text-stone-300" required />
</div>
<div class="form-control">
<label class="label">
<span class="label-text">{move_tr!("password")}</span>
</label>
<input type="password" name="form[password]" placeholder=move_tr!("password-placeholder") class="input input-bordered text-stone-300" required />
<label class="label">
<a href="#" class="label-text-alt link link-hover">{move_tr!("forgot-password")}</a>
</label>
</div>
<div class="form-control mt-6">
<input type="submit" class="btn btn-primary" value=move_tr!("login") />
</div>
</ActionForm>
}
} |
Beta Was this translation helpful? Give feedback.
Replies: 2 comments 5 replies
-
By looking at #2069 I learned that actively calling the redirect function on server fn can prevent the default |
Beta Was this translation helpful? Give feedback.
-
None of the code you've given above will be interactive in the browser, because there is no Consider making |
Beta Was this translation helpful? Give feedback.
To give more detail about what I mean, if that's helpful: in islands mode, none of your components are compiled for or run on the client, unless they are specifically marked as
#[island]
.LoginForm
is not, so no client-side code runs. A<form>
like the one created by theActionForm
component has its own default HTML behavior, which is what you're seeing. In the case of a<form>
submitting directly to a server function, the server function is designed to redirect back to theReferer
if no other redirect is set (so that the form submission does not simply land on the server function endpoint and stay there.)In the case of an error, it will redirect back to the
Referer
with the error encode…