Hide Form When Logged Out0:00
The form to add a new Puppy is the last component that we haven't converted to Inertia in our application. So let's tackle that right now. All right, to get us started, we should handle the visibility status of this form. So right now you can see that I'm logged out and if I scroll down I can see the new Puppy form, which really doesn't make sense. You should only be able to create a new Puppy if you have a User account.
You should only be able to create a new Puppy if you have a User account. So in the index route for the Puppies, just like we are doing already for the short list, we are checking if we have all that User and we are going to do exactly the same for the new Puppy form. Alright? And so now we should not see the new Puppy form because I'm logged out and indeed I'm at the bottom of the page here.
because I'm logged out and indeed I'm at the bottom of the page here and there is no new puppy form to be found. Perfect. Alright, so let's go and log in the application with simon@laracasts.com. And remember when we seeded the hundred puppies, I kinda wiped the database so the User got created but there is no liked puppies. So let's go like a few pups just to have some realistic looking data.
Shortlist Pagination Bug1:21
So let's go like a few pups just to have some realistic looking data. We might go to the next page and maybe like rusty and a bru. No. Alright, so I should have five liked puppies now and haha, interesting bug here, uh, because we have the pagination, the short list is only going to show the liked puppies from the current page. So now I am on page three, but if I go back to the homepage.
So now I am on page three, but if I go back to the homepage where I liked a few other puppies, you can see that they only show here. So that's interesting. Let's park that one for right now. But when we do a little round of refactoring on our app, we should definitely make sure that we fix that up. So instead of passing the paginated puppies to the shortList component, we should pass all the liked puppies for the User.
to the short list component, we should pass all the liked puppies for the User. All right? So like I said, let's file that away for now, but we definitely will come back to fix this and to make sure that we do, let's go in the short list and add a big to-do comment. Make sure all the liked puppies are showing, not just the ones from the current page. Alright? And this increases the chances that we remember to go back and do this.
Refactor Form for Inertia2:35
Alright? And this increases the chances that we remember to go back and do this. Okay, but now that I'm logged in as Simon, I should after the poppies list, see the new puppy form and yep, there it is. So let's now go inside this components and make it work better with Inertia share. First of all, we are using a createPuppy function that comes from our queries and this is where we call the external API as a POST method.
that comes from our queries and this is where we call the external API as a POST method to basically create a new Puppy in the system. So we are not going to do this, we're going to do this in-house next. You can see that it's using useForm status from React, but because we are using Inertia, we can use the useForm helper which has a processing boolean as you've seen before. And also we'll end up getting rid
bullion as you've seen before. And also we'll end up getting rid of the puppies set puppies state like we have done for the rest of the component. This is the last place in our application that's using this and at the end of this lesson we will no longer use that, which means we can also get rid of that and we won't use the ErrorBoundary. So basically I think we're going to get rid of all these imports by the end of the lesson.
So basically I think we're going to get rid of all these imports by the end of the lesson. So let's start, uh, dismantling this application. So I will get rid of the ErrorBoundary to start with there. And here's another interesting part where we had the SubmitButton in an external component here instead of directly in the form. And the reason we had done that is so that we could use react's useFormStatus to basically have the pending state.
that we could use react-use-form-status to basically have the pending state. It only works if it's a child component, but because we're going to use Inertia's processing uh, information instead we will be able to move this submit button right there with the form. Alright, so that's the overview of the changes that we want to make to this form component. So let's get started. Alright, I can get rid of the error boundary import since we've removed that.
So let's get started. Alright, I can get rid of the error boundary import since we've removed that. And so what I wanna do here is remove the form action that we were using in our vanilla React implementation and instead I will have an onSubmit event. So let's just get a void function for now, but we are going to fill that up as we go. And straightaway you can see that the action was the only place where we were using the puppies or setPuppies.
that the action was the only place where we were using the puppies or setPuppies. So I can remove the whole state here, which means I can remove the puppyType and the setPuppyType as well, which means guess what, we are no longer using any puppies and setPuppies state in any parts of the application. So I can go in the index.tsx and get rid of that state completely. Finally. So in index.tsx,
and get rid of that state completely. Finally. So in index.tsx, once we stop passing the puppies and set puppies state to the new puppy form, you'll see that now we're being told that we are actually not doing anything with our useState so I can get rid of this completely. And so this marks a pretty important milestone in our application. We have completely stopped using local state,
milestone in our application. We have completely stopped using local state, we had the puppies state and the search query state and now none of this exists anymore and we basically get all our state from the actual data from the backend instead of trying to recreate the state of what's being pulled from an external API. So this is much simpler to reason about and I think that we are in a pretty good place. How nice does this feel?
and I think that we are in a pretty good place. How nice does this feel? We have paginated puppies and filters coming from Inertia from the backend and then none of the state is used in any of the components. Great. So let's go back to our new puppy form and let's look at the actual form itself. So we are going to have an onSubmit event that will populate and then we have a series of fields. The first field is the name field here, it is required.
that will populate and then we have a series of fields. The first field is the name field here, it is required. Then we have the trait field, which is also required. And the third field we have is the image_url. And so we are also going to make this required because we don't want to have a cute puppy without an actual photo. And you know what the field is called image_url because this is the name of the field in the database. But really this is not a URL that we are uploading.
because this is the name of the field in the database. But really this is not a URL that we are uploading. It's an actual image. So I think we're gonna change the field name to just image, but then we'll do the conversion in the backend to store to the image_url field. So this becomes image, uh, the ID can be image as well. And of course the htmlFor that is bound to the ID should be image as well. Okay? So because we've removed the form action from the
to the ID should be image as well. Okay? So because we've removed the form action from the previous implementation, what I'm expecting is when we submit the form right now, it should just do a GET request and the field values will be in the URL in the query parameters. So let's check it out, let's create a Puppy called Simon. Cool. And we'll choose an image. Let's go with that one. Hello you. Uh, and so we'll select this and when I click add Puppy,
Hello you. Uh, and so we'll select this and when I click addPuppy, it should have done a GET request. So if I look at the URL, we can see the fields appended to the URL. Okay? So that's the default form behavior if nothing is specified. But what we wanna do now is instead of a GET request, we want to do a POST request and we want to decide which endpoint in our backend we want.
Submit Form via POST7:44
we want to do a POST request and we want to decide which endpoint in our backend we want to hit with that POST request. All right, so let's go to the top to our form element. And in a traditional form you would have a method and set that to POST. But here we're going to use Inertia's, useForm helper to do a bunch of useful things. So let's import useForm from Inertia and here we'll extract a few things from the useForm hook.
So let's import useForm from Inertia and here we'll extract a few things from the useForm hook. One of them that we want is the post action because we're going to post a form. And so basically now we can in a form onSubmit, capture the event and then prevent the default with e that prevents default. And then we are going to use this post to post to a specific route. And yes, we will call this puppies.store.
to a specific route. And yes, we will call this puppies that store. So that route doesn't exist yet, so this is not going to work. But if we try submit the form once again, we should actually see that it's attempting a POST request and we should get an error because this route doesn't exist. Alright, so let's go to the bottom once again Simon. Cool. Pick the same image because why not? What about this one? This is pretty cool. And so when we go add puppy,
Pick the same image because why not? What about this one? This is pretty cool. And so when we go add puppy, nothing happens, but let's look in the console and you can see that puppies, that store is not in the rats list. So when I clicked add puppy, it's triggered this error like I've just done once again. Alright, so what's our next logical step from here? Well I guess we can go in the PuppyController and create the store action and route.
Add Store Route and Action9:13
Well I guess we can go in the PuppyController and create the store action and route. And to refresh your memory, we have an index action, we have a like action and now we are going to create a store action. So because we have multiple actions here, I just want to quickly add index. And this is sort of a redundant comment because it just tells the name of the action but it creates some nice visual separation.
because it just tells the name of the action but it creates some nice visual separation between the methods, which I do like. Alright, so now when I scroll it's pretty easy to see where the different actions begin. So public function store, again, we'll try to do it without copilot, which is going to be impossible. So maybe let's receive the $request and just dd whatever we receive. dd($request).
and dump whatever we receive. Didi request. Yeah, all which should show us all the fields submitted. Next, let's go in web.php, do php and connect this to a route. So web.php, we want to be logged in for this. So we have the Puppies. And now exactly like this, do a POST request which hits the PuppiesController store method that we've just created. And we are going to name that route puppies.store,
method that we've just created. And we are going to name that route puppies that store, which is what's expected because we use this on the front end. Alright, so now let's go and submit the form again and see what comes out of this Simon. Not cool. And we are going to select an image. Let's use this one once again and add puppy. All right, so we have an empty array, which means that we haven't sent any of the fields to the request.
Bind Fields with useForm10:47
All right, so we have an empty array, which means that we haven't sent any of the fields to the request. So let's flesh out a little bit our useForm hook so we can actually have the data from the form and pass it in the request. Alright, so back to the new Puppy form component we go. And what we want to do here in our useForm hook is define some field values. So I'll have an object And remember our fields are named name, trait and image.
So I'll have an object And remember our fields are named name, trait and image. So here we will have name set to an empty string and then yep, trait and image sets to null. So now we want a mechanism to be able to populate these fields and we can extract another helper from the useForm hook setData. And we also going to have the data itself so we can access that data.
And we also going to have the data itself so we can access that data. And here's where things are going to start becoming interesting. We are going to connect each field with the data value from the three values that we've set in our useForm hook. So we have name, trait, and image. And so for the name field here, I will add a value, which I'm going to bind to data.
And so for the name field here, I will add a value, which I'm going to bind to data that. And you can see that the useForm hook knows that data will have access to the fields we've defined. So name here is what we want. So setting a value attribute like this on an input is going to turn it into a controlled input in React. And as you can see, uh, copilot makes a great recommendation. A field that has a value also needs an
copilot makes a great recommendation. A field that has a value also needs an unchanged to be functional. So unchanged we are going to accept the event and then set data for the name field to event.target that value. So I will accept this suggestion, which is perfect. And so now if we scroll to the next field, copilot understands the assignment and has suggested the exact same change, we set a value.
copilot understands the assignment and has suggested the exact same change, we set a value to $data that trait and then on change we update that value. So I'll accept that once again. And for the last one for the input, it's a little bit different because input types of files but unchanged. We want to set the image to $targetFiles. And the first one, because it's going to be an array of files, I believe that this might not always be the case.
And the first one, because it's going to be an array of files, I believe that this might not always be the case. Yep. targetFiles is possibly no. So we need to check if target has files and if it does, we are going to set the first index of this file. Otherwise we're going to set it to null. And so basically TypeScript is now telling us, hey, you either define a file or null, but I'm expecting null here.
you either define a file or null, but I'm expecting null here. And the reason it's expecting null is at the top here we've set the image to null. So we need to tell that this is null, but as file or null and TypeScript should be happy about this now. So if we go back down, we don't have any type errors anymore. Alright, so we have connected all our fields to the data values that they are going to hold.
Alright, so we have connected all our fields to the data values that they are going to hold and use setData to update that value when the field input changes. So let's try fill the form one more time and submit it and see what happens. I will refresh the page once again, go Simon, super cool this time and select our image and submit that. And haha check this out. This time we have successfully submitted the name, the trade.
And haha check this out. This time we have successfully submitted the name, the Trade and the Image, which is an uploaded file with a bunch of fields attached to it. So that is great news. We can now go back in the PuppyController and start fleshing out what the backend should do when it receives the information that we've submitted with the form.
that we've submitted with the form.
