form helper data response without redirect #568
Replies: 15 comments 11 replies
-
Maybe a this.form.post(route('posts.store'), {
errorBag: 'createPost',
wantsJson: true,
onSuccess: (data) => {
this.$emit('success', data);
}
}); And then in the controller you can return json if it wants it: if(request()->wantsJson()) {
return $post;
}
return Inertia::render() This would allow the same form to work as a page and modal |
Beta Was this translation helpful? Give feedback.
-
Another example of when you'd want to return data from <app-layout>
<comment-list :comments="comments" />
<create-comment-form @success="comments.push($event)" />
</app-layout> this.form.post(route('comments.store'), {
errorBag: 'createComment',
wantsJson: true,
onSuccess: (data) => {
this.$emit('success', data);
}
}); |
Beta Was this translation helpful? Give feedback.
-
This would be something for the upcoming dialogs feature. Until the feature is ready, you could already Then watch this prop in your parent component to update/push to your existing comments data or even return the whole commnents - depending on how much comments data you have. Hope this helps ! |
Beta Was this translation helpful? Give feedback.
-
The best way in this case is the use Axios |
Beta Was this translation helpful? Give feedback.
-
I have a use case for this behavior. Passing a |
Beta Was this translation helpful? Give feedback.
-
I made this quick wrapper around useForm helper to make API/JSON Calls: https://gist.github.com/mohitmamoria/91da6f30d9610b211248225c9e52bebe |
Beta Was this translation helpful? Give feedback.
-
I too would love to be able to submit a form and get JSON back without having to jump through a bunch of hoops. |
Beta Was this translation helpful? Give feedback.
-
So while I get why this is wanted, and maybe we can figure out a solution that makes sense, but my gut reaction to this is that it goes against some of the core architectural principles of Inertia. Architecturally Inertia isn't an alternative to XHR (Axios) or fetch, which are typically design for asynchronous requests. Rather Inertia is designed to mimic classic full page reloads that you find in server-side rendered applications. Inertia request are synchronous — only one can happen at a time. This is just like standard browser visits. When you click a link, you go to another page. Only one visit can happen at a time. If you click another link while the first visiting is still loading, the first one is cancelled and the second visit takes its place. Plus each request results in a "full page response"...you've landed on another page in the application. This is pretty core to how Inertia works. So, looking at @dillingham's example, I can see why this is a tricky problem to solve. There are two forms on the page — one to create a new post, and one to create a new user, which presumably once a new user is created you want that person to be selected as the user for the new post. If you think about how you'd build that using classic full page form submissions, it gives you some ideas of how you can do that with Inertia:
I actually recently created a shortcut for this in one of my apps, which I called <?php
class UsersController
{
public function store()
{
$input = Request::validate([/* ... */]);
$user = User::create($input);
Inertia::flash(['new_user_id' => $user->id]);
return Redirect::back();
}
} Now when a new user is created, the This honestly works awesome, but it feels pretty opinionated, so I'm not sure if it makes sense to bake into the Laravel adapter yet. It's super trivial to implement in your own apps though. First create the macro: use Inertia\ResponseFactory;
use Illuminate\Support\Facades\Session;
ResponseFactory::macro('flash', function ($data) {
Session::flash('inertia', $data);
}); Next, update the public function share(Request $request): array
{
- return array_merge(parent::share($request), [
+ return array_merge(parent::share($request), $request->session()->get('inertia', []), [
// Here's an example watcher in Vue.js for how to watch a "flashed prop" like this: (this is sudo code) const props = defineProps({
users: Array,
new_user_id: Number,
})
const createPostForm = useForm({
title: '',
user_id: null,
body: '',
})
watch(
() => props.new_user_id,
(userId) => {
createPostForm.user_id = userId
},
) I hope that helps explain why Inertia is built the way it is, and gives you some ideas of how to work through this particular problem. I'm not opposed to other solutions, but I think one of the awesome things about Inertia — and one of the things that makes it so simple — is how every Inertia request results in a standard full page Inertia response, and I legit worry what it might do to the project to make it behave more like XHR or fetch. And I know ya'll know this, but it's 100% okay to just use XHR or fetch sometimes in an Inertia app. These are not mutually exclusive. In my apps 99% of my requests are done using Inertia, but there are certain situations, such as polling, where the synchronous nature of Inertia doesn't make sense and I'll reach for XHR instead. |
Beta Was this translation helpful? Give feedback.
-
Thanks for the quick reply @reinink! I totally get your hesitation to expand the scope on this particular issue. I think the thing I'm looking for here is the ability to use the form helper and all its niceties but skip the Inertia routing layer, if required. I can totally eject and use XHR or fetch, but then I'd have to rebuild all the error handling, busy state, etc. I ended up going with a modified version of this (#568 (comment)) solution. It's a bit fragile because it recreates some of the internal form stuff, but at least it's consistent now. I have a // Test the connection
this.form.post('/connections/test', {
api: true,
});
// Store the connection
this.form.post('/connections'); Both use the form, both get the loading states, errors, etc. I'd love if there were a way to do this natively! |
Beta Was this translation helpful? Give feedback.
-
@reinink I've been thinking about this some more and this line is the line that stuck out to me from your response:
I agree with that, but I don't want this particular request to be an Inertia request! But I do want to use the form helper. I think you could give us an escape hatch by making the submit method editable from the outside: https://github.com/inertiajs/inertia/blob/master/packages/vue3/src/useForm.ts#L202-L206 Even changing |
Beta Was this translation helpful? Give feedback.
-
You are right, Aaron. The router is the single piece that makes it
difficult to make the API/JSON calls.
If we replace it while still wanting the busy states, error states etc,
you’ll realize that we will have to change the “options” parameter and
rewrite it for axios/fetch.
My gist earlier in above comments does the bare minimum stuff to get the
form helper working with API/JSON calls.
On Wed, 5 Jul 2023 at 5:42 PM, Aaron Francis ***@***.***> wrote:
@reinink <https://github.com/reinink> I've been thinking about this some
more and this line is the line that stuck out to me from your response:
Inertia request are synchronous — only one can happen at a time.
I agree with that, but I don't want this particular request to be an
Inertia request! But I *do* want to use the form helper. I think you
could give us an escape hatch by making the submit method editable from the
outside:
https://github.com/inertiajs/inertia/blob/master/packages/vue3/src/useForm.ts#L202-L206
Even changing router to this.router would give me a chance to overwrite
it and do my own thing for an API call vs Inertia call. Just a thought!
—
Reply to this email directly, view it on GitHub
<#568 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/AAVBAVVWSNQ343A6VT7KYDLXOVK4PANCNFSM4ZUHYZRA>
.
You are receiving this because you commented.Message ID:
***@***.***>
--
– mohit
|
Beta Was this translation helpful? Give feedback.
-
Hi, Is there any new progress? In fact, what we want is not to redirect, regardless of what the response is, right? I tried an implementation that simply added a parameter that did not visit the page, that is, did not redirect. For more detail can see my fork submission. But this makes the core code look distorted. Hopefully, it will lead to a perfect way of doing it. |
Beta Was this translation helpful? Give feedback.
-
@aarondfrancis - I've been back and forth the last few days wondering if I'm overlooking something obvious and I agree with this.
I'm having to do context updates to the form (based on fields the user is filling out as they go along) and having to hop out of the form helper felt like i was doing something wrong, because of the fact we already have state there. It feels like inertia wants you to either subdivide your form into different page stages (different components / different routes - which isn't ideal for the forms i'm working on) or manage a "primary" formHelper and context-updating xhr calls that have their own "form helper" functionality rolled by you on a case by case. Which, may just be how it has to be. Anecdotally, looking through posts about passing data in inertia it seems like this is where a few people get hung up. As @reinink stated, we know we can reach for xhr but I think these paper cuts make it feel like you shouldn't be? When you should? Just felt like I was duplicating functionality and adding complexity when it felt like i should just be able to say "Hey, formHelper, do a "basic" request because I don't want you thinking you need to do any page routing here." Appreciate the detailed explanations @reinink! Thanks for all you do. |
Beta Was this translation helpful? Give feedback.
-
I really didn't want to re-invent the wheel here, so I did a dirty hack using laravel precognition as I use that in all my other places anyway. For my use case, I basically wanted a message to save incase you refreshed the page or went away and came back.
IMHO, this would be an amazing feature to be built in, not because its truely needed but it keeps the code for all forms the same, even when you aren't needing redirects. However, I honestly wouldn't mind if there was another package like the laravel precognition one that has essentially the same api to keep things simple. Not having to mess with axios and do error handling / validation is a real time saver |
Beta Was this translation helpful? Give feedback.
-
I don't have exactly the same issue as wanting the response as JSON but I figured I'd also share my own story since I also kinda need to use the form-helper without the redirection part: Each row on this table has an "Edit" button which opens up a modal to do any changes you want, click save and close the modal. The way I do this is using an public function index(Request $request)
{
return Inertia::render('Customer/Index', [
'customers' => fn () => /* ... query with filters and pagination */,
'editCustomer' => fn () => isset($request->query('editId')) ? $queryCustomer->get($request->query('editId')) : null,
]);
} My react Index component looks like this: export default function Page({
customers,
editCustomer
}: {
customers: PaginatedCollection<PaginationCustomerData>
editCustomer: CustomerData | null
}) {
return <AuthenticatedLayout>
<DataTable data={customers}/>
<EditCustomerDialog customer={editCustomer}/>
</AuthenticatedLayout>
} The edit button is inside the <Button onClick={() => {
router.reload({
only: ['editCustomer'],
data: {
editId: customer.id
}
})
}}>Edit</Button> So when clicked, it will make a visit to the same index page with the query param, allowing a fetch to be made. export default function EditCustomerDialog({
customer
}: {
customer: CustomerData | null
}) {
const {data, setData, errors, put, reset, setDefaults} = useForm({
name: "",
email: ""
});
useEffect(() => {
if (customer) {
setData({
name: customer.name,
email: customer.email || "",
points: customer.points.toString()
})
}
}, [customer]);
// .. other code ..
return <Dialog open={customer !== null} ....... The problem arises when doing the submit const onCreateSubmit = (e: FormEvent<HTMLFormElement>) => {
e.preventDefault();
put(route("customers.update", customer?.id), {
preserveScroll: true,
onSuccess: () => {
router.visit("", {
replace: true,
preserveState: true,
data: {editId: undefined}
});
}
});
} The problem with this is that it will do a double-fetch/navigation. Once the Ideally, I would wanna ignore the redirect from the controller (even better: just return a 200 and not do a navigation) and handle the navigation on my own. One solution would be to use axios, but then I lose all the useful stuff from the I'm not sure of any other solutions here, and it would be helpful to tell inertia "hey, it's okay I'll do it manually" |
Beta Was this translation helpful? Give feedback.
-
How do you submit a form using the awesome helper and not redirect?
I want to return the newly created model and pass it as a prop to a different component after.
I know I can use axios but It breaks continuity and seems like how I would assume the form helper to work
Here is a quick example to demonstrate using vue to achieve this
https://twitter.com/Philo01/status/1373646016189173765
TLDR: #568 (reply in thread)
Would be cool if when onSuccess is defined, it doesn't redirect.
Ideally, onSuccess would receive
data
from Controller@storeBeta Was this translation helpful? Give feedback.
All reactions