Building Comment Form UI0:00
Alright, part two of our comment functionality. It's time to build the UI, connect it to the backend that we created in the last episode, and actually have our first working form on Framerate. Let's get going. So on our show page, perhaps just under the comment heading here, we could have our form. We're using Inertia and Vue, so we don't need an action. Instead, we're going to override the submit.prevent hook, and we'll call the action to actually submit our form, addComment. Okay, inside here we can have a div, and we'll need, first of all, a label. Because we're using Jetstream, we can make use of Jetstream's components that are built in out of the box, save us doing that manually. So here's an input label. We'll call the input body, and then let's just say comment as the label itself. We should have a textArea here, but I'm actually
save us doing that manually. So here's an input label. We'll call the input body, and then let's just say comment as the label itself. We should have a text area here, but I'm actually going to reach for the text input for now, and then we'll switch it out in just a moment. Let's give this an ID of body so that the label is correctly linked up, and we'll need to set the v-model, but we'll come back to that in a moment. Okay, finally, we'll need our submit button. We can make use of the primary button for this. Let's give this a type of submit so that it can actually submit our form. We'll have mt-3 to give a bit of spacing above, and then add comment will be our call to action. Let's go down to the script setup now, and we created a method called addComment, didn't we? That's what we wanted to call it. This is going to perform our actual logic, but
Submitting With Inertia Form1:26
Let's go down to the script setup now, and we created a method called addComment, didn't we? That's what we wanted to call it. This is going to perform our actual logic, but how Inertia works is we need to build up a form object that we will then send to the back end. We'll call this the commentForm, and it uses the form helper that Inertia ships with. Super useful. You pass it an object with any form fields that you want to send to Laravel. We'll create one called body, which by default is an empty string, and then in order to send that commentForm, we can use the post method to post it to the posts.comments.store URL, and we can grab the post from props, so props.post.id as the information there. That should actually work without any issues, I should imagine. We just need to wire up our text input to this commentForm body prop. We'll come back up to the text input here,
That should actually work without any issues, I should imagine. We just need to wire up our textInput to this commentForm.body prop. We'll come back up to the textInput here, and we'll set the model to commentForm.body. Should we see what that looks like in the browser? So it definitely needs some work and love here, but let's just test out putting something like helloWorld in place, adding a comment, and look at that. We have our very first comment. Now there are some styling issues that need to be addressed, but there are a couple of things I'll tackle first. First is note that when we post a comment, it jumps back to the top of the page. I want to prevent that. We should stay at the same level on the page. We can easily do that by preserving the scroll. So when I use the post method here, I can pass a second parameter, which is an object of options, and there's
Improving Form UX3:00
level on the page. We can easily do that by preserving the scroll. So when I use the post method here, I can pass a second parameter, which is an object of options, and there's a preserve scroll option that we can set to true. And now if we say hi there, and we post that comment, note that we don't actually move on the page at all, and we immediately see our comment appear down below. Good start. The next thing I want to tackle is this comment label, which I think visually is superfluous to requirements and could probably be instead handled with a little placeholder on our input. So we still want it there for accessibility purposes, but it's not necessary visually. We can actually tackle this using a Tailwind helper called srOnly, which is short for screen reader only, and you can see that now it completely disappears, but it's still in the DOM and screen readers will still pick up.
Creating a TextArea Component3:44
helper called SROnly, which is short for screen reader only, and you can see that now it completely disappears, but it's still in the DOM and screen readers will still pick up on it. Very useful. Let's set a placeholder on that text input. So placeholder, and let's be a little bit playful, speak your mind Spock. How about that? Does that work nicely? Of course it does. Now I think this would be a good time to switch from using a text input to a text area, which will be much more appropriate for posting a long form comment. Because these are wrapped components, if we jump into text input, you'll see it's quite complex really in that we defer the model value. So binding the v-model down into the component and then fire it back up based on what takes place on this input here. Now, if you're not working with Vue, you'll have to handle this differently depending on what framework you're using,
fire it back up based on what takes place on this input here. Now, if you're not working with Vue, you'll have to handle this differently depending on what framework you're using, but suffice to say, rather than create textarea myself, I'm just going to copy the base work that's been put into text input and then alter it very slightly. So we'll copy and paste to a component called TextArea. And then inside TextArea, I'll change the input to textarea like so. And we'll also want to paste in our own classes, which I'm going to grab from Tailwind UI. Okay. I think everything else on there should work as expected. So if we go back to PostShow, I can change text input for textarea and element is missing an end tag. Of course, if we come back to TextArea, because of how textarea works, you have to have a closing tag on it. Let's try that again. Much better. That's a lot
an end tag. Of course, if we come back to textarea, because of how textarea works, you have to have a closing tag on it. Let's try that again. Much better. That's a lot cleaner. And I'll tell you what we'll do. We'll go back to Post show and we'll set a row of four on this just to make it a little bit deeper. So rows equals four. And you can see you have a little bit more room to type your comment. I don't want to spend too much time on this textarea component because down the line, we're going to be very much changing this out to allow for markdown and formatting of comments. But we'll come back to that later on. This gives us a nice base to start on. You'll also notice that if I post a comment, so say nice post and add a comment, it still maintains the content in the comment box here. And we don't want to do that. If we are successful in posting a comment, we should reset the
Preventing Duplicates and Errors5:57
so say nice Post and add a Comment, it still maintains the content in the comment box here. And we don't want to do that. If we are successful in posting a Comment, we should reset the form so that you don't accidentally post the same Comment twice. In order to do that, we can come down here like so and we can hook into the on success handler. So on success, we want to take the comment form and we want to reset it like so. And now when you say nice Post and you add the Comment, the form field, the text area clears itself out ready for you to post another one if you so desire. Let's add a little bit of spacing above the form before that heading. Come back up to our form tag up here and let's say class equals maybe mt-4. Very nice. And already this is looking great. There is another issue we should tackle to prevent spam on our posts. So if I come up to the network tab here and I'm going
maybe MT4. Very nice. And already this is looking great. There is another issue we should tackle to prevent spam on our posts. So if I come up to the network tab here and I'm going to set it to a slow 3G connection, and then I'm going to type in a comment, something like nice job, but I hate this film. And then we'll add the comment. And it's taking a while because we're on slow 3G, but the person just keeps hitting add comment because they don't realize it's their fault and their internet connection. What's going to happen is all of those requests are going to go through eventually. And you can see we just have this same comment over and over again, spamming our database. We do not want to allow that to happen. So what we're going to do instead is we will disable this button from working if a request is currently taking place. And you can do that using the disabled tag, using
to happen. So what we're going to do instead is we will disable this button from working if a request is currently taking place. And you can do that using the disabled tag, using commentForm.processing. So this is a really cool property on any form helper object from Inertia. Whenever a request is in process, processing will be set to true. And you can use that for all sorts of interesting things. But in our case, now if we have another comment like "great work, lovely review," and then we hit add comment a million times, it's still only going to post a single comment. "Great work, lovely review," as you can see. Now one of the things we added in the previous episode was validation errors, right? So validation for this body. So required was one. What happens if we click add comment and we don't put anything in the body? Well, nothing's happening per se. There was a slight update there because
for this body. So required was one. What happens if we click add comment and we don't put anything in the body? Well, nothing's happening per se. There was a slight update there because the timestamps changed. But nothing's happening. We're not posting comments. And I would imagine if we go to the network tab and we run this again, yeah, we're getting a 302. So it's redirecting us. And I imagine there are validation errors in the payload. But we're not showing those validation errors anywhere on the front end. And we need to do that so that the user knows what they're doing wrong. So we can come back up to this text area component. And underneath, let's make use of the input error component that JetStream ships with, which accepts a message. And you can access any errors with a request using the form.errors.body property. In our case, it will be the body property. And
JetStream ships with, which accepts a message. And you can access any errors with a request using the form.errors.field property. In our case, it will be the body property. And hopefully now when we add a comment, it tells us actually the body field's required and you didn't provide it. And in order just to give us a little bit of room to play with, let's add margin-top there, which just provides a little bit more space. And that looks really nice. So that's the required validation field. We added the string validation as well. That won't be a problem if people are using the application as they should, because textarea only sends strings to the back end. But remember, we had that max character length of 2500. Should we just see if that works? Now, when I need anything like this, I like to employ a little trick on the terminal, and that is to run an inline php statement. So we can
Should we just see if that works? Now, when I need anything like this, I like to employ a little trick on the terminal, and that is to run an inline php statement. So we can use this -r flag when running php in order to execute any arbitrary PHP code. In this case, I'm going to echo out a string repeat of the A character 2501 times. And remember, we sort of did that in the previous episode in order to create our validation rule check in the test. I'm just doing the same thing now, but from the front end. And then I'm piping that into pbcopy, which will copy it to our clipboard. Of course, if you're using a different operating system, and this won't work for you, you'll need to do something slightly different. But there is always a way to run something like this. So when I hit enter, we can jump back into our application, and I'll paste it into our comment form. Look
slightly different. But there is always a way to run something like this. So when I hit enter, we can jump back into our application, and I'll paste it into our comment form. Look at that. We have 2501 A's in a line. We can go ahead and click add comment. It's going to come back with a validation error saying, well, you can only actually post 2500 characters. But if we get rid of just one of those characters and click add comment again, now it's going to paste without any issues. And you can see we have a slight CSS problem here, because if you have a super long word like this, it will break the page, and we don't want that for our users. So I believe we can just update the comment here and change this P. And is it break-all? Let's have a look at break-all. There we go. So that will break the word up nicely. And even if you have a super long word, it will still only take up the max width.
Hiding Form for Guests11:08
it break all? Let's have a look at break all. There we go. So that will break the word up nicely. And even if you have a super long word, it will still only take up the max width of its container. Brilliant. So this is looking really good. We have successfully wired up a front end and a back end, and users are able to come in and post a comment. But that does remind me only users can post a comment. So I have logged in as myself. What if I log out and then we head back to this post? So posts, we'll jump back in here. Yeah, take a look at this. We still see the form. And if I say hello world, and I add the comment, it's not going to work, it will throw an error, but I shouldn't even see the form. And that's just giving me something that I know I can't access. And I don't want users or guests ringing up complaining that the form doesn't work for them. That's just an issue I don't need.
just giving me something that I know I can't access. And I don't want users or guests ringing up complaining that the form doesn't work for them. That's just an issue I don't need to deal with. I can hide it for guests and only show it for users. So let's jump back into our application code. We'll go to PostShow. And on this form, I'm only going to show the form if the pageProps.authUser component actually exists. So now the form has gone completely because I'm a guest. But if I were to log in and log back in as test@example.com and password, let's go back to posts and back to this post. Now I see my comment form again. And sure enough, if I type in hello world and add a comment, it will appear as normal down below. So there we go feature complete kind of I mean, there's quite a lot we need to do before we can call this feature complete. One of the most common
will appear as normal down below. So there we go feature complete kind of I mean, there's quite a lot we need to do before we can call this feature complete. One of the most common things we do on the internet. I'm Luke from the future. Faster, stronger, with shorter hair. Due to a massive security hole opened up in the last two episodes, the future of humanity is at stake. Come with me if you want to live.
