Seed comments with Tinker0:01
Okay, let's fix that UI bug I was talking about with regards to pagination and then we'll style our buttons. In order to show you what the issue is, I need a load of comments that I have actually created. Rather than doing that manually, let's go ahead and do that using Tinker. So I'll head into our terminal, php artisan tinker, and this will give us what's known as a REPL, which allows us to write PHP code and, more specifically, Laravel code inside of an application shell. So the first thing we'll need to do is grab the correct Post, which I'll do using Post::find. And what is it, 245 here, so 245, and that will assign it to the post variable so that we can then use the comment factory. Let's set 50 comments on this post, so I'll use the for method. Pass in the post, and then we'll call create and we'll set the user_id to my user_id, which is 11.
Reproduce pagination reset bug0:49
Let's set 50 comments on this Post, so I'll use the for method. Pass in the post, and then we'll call create and we'll set the userID to my userID, which is 11. If that's worked correctly, we should refresh this page and now, yeah, look at that. We have five pages of comments all created by me, meaning I can delete these as I see fit. Now watch what happens if I go onto page two and delete one of these comments. Note the URL at the top, I'll hit delete, and the page query has disappeared. If I scroll down to the bottom, you'll see we're back on page one again. What's going on? Well, if we go back to our code and I take a look at the destroy method on the CommentController, note that we redirect to the post show route passing in the post, but we don't include any page number.
Redirect with page parameter1:32
Well, if we go back to our code and I take a look at the destroy method on the CommentController, note that we redirect to the post show route passing in the post, but we don't include any page number. So basically, it will reset back to page one. To prove that that's the case, I'm going to pass this as a post property, and I'll also pass in a page property, which will end up as a query parameter because of how routes work, and then let's go ahead and delete another comment, perhaps this one here. Now, note that we're redirected to page two, and sure enough, the paginator at the bottom of the page confirms that. So obviously, whilst we cannot hard code this route parameter, we do need to inform the destroy method that we want to redirect to a certain page. We could pass it in to the request, so we could say request,
we do need to inform the destroy method that we want to redirect to a certain page. We could pass it in to the request, so we could say request, and then we'll grab an item from the query, and of course, we want something called page. And if that isn't there, well, it will automatically set itself to null, which will be the same as saying page one. So that should work well, but how do we actually send that up to the backend? Well, why don't we jump into the comment endpoint here, and here is where we actually make the request, router.delete. We create this route for comments.destroy, and much the same as we've done before, we can create an object where we set the comment property directly,
We create this route for comments.destroy, and much the same as we've done before, we can create an object where we set the comment property directly, and then we can also add a page property, and let's set that to 2 this time. So now we're hard coding this. Let's see what happens when we come back in and we start deleting certain comments. Well, it got deleted, and it set the page to 2. Let's update this to 3 instead, and we'll do the same thing. So I'll come in, delete, and now we're on page 3. This seems like the perfect solution. All we need to do is remove the hard coding and make it dynamic.
Refactor delete to parent3:19
This seems like the perfect solution. All we need to do is remove the hard coding and make it dynamic. The other thing you'll note is that currently we're performing the request inside this Comment view component. That's bad practice. I don't really like to use that word because it's not necessarily bad practice, but it makes sense that all of our routing would happen in a single file. So we should really make this request one component higher on the show page. No issues. We can do that. All we have to do is define an event inside this little Comment component.
No issues. We can do that. All we have to do is define an event inside this little Comment component. Let's say const emit equals defineEmit, and we'll create a delete event that we want to fire. And, well, when we actually submit this form, rather than deleting the comment, we're going to emit the delete event passing the comment.id as a parameter. Now we can intercept that on the show page. Let's implement that functionality. So we'll head into PostShow,
Let's implement that functionality. So we'll head into Post show, and up here where we have our Comment component, we're going to capture it by saying @delete equals, and then we'll have a method called deleteComment that we'll define down here. We can steal the majority of the functionality from deleteComment from this constant here, which can now be removed from our Comment component, and we'll paste it down here underneath addComment. So we have addComment and deleteComment in one location. Now, when we define the event,
So we have addComment and deleteComment in one location. Now, when we define the event, we said that we're passing the commentId in, so we can accept the commentId, and then instead of hard coding this here as props.comment.id, which is no longer appropriate, I'll pass the commentId in directly like so. The page is now accessible using the comments property. So, tell you what, let's go ahead and take a look at that in the browser. I'll open up Vue DevTools again.
So, tell you what, let's go ahead and take a look at that in the browser. I'll open up Vue DevTools again. Go down here, and we should have the show page. We have comments, meta, currentPage. That will be perfect for sending the information to the backend. So, in our code where we currently hard code the page, well, we can set that to props.comments.meta.currentPage, and that should work absolutely fine. Okay, shall we see if our refactor is successful? I'm going to come to the standard console so that we can see if any issues arise.
Okay, shall we see if our refactor is successful? I'm going to come to the standard console so that we can see if any issues arise. I'll refresh just to be sure everything's working correctly, and we're currently on page three. If I delete one of these items, we are still on page three. Awesome. Check that out. So, we have successfully fixed this UI issue. If I go to page two and delete a Comment there, then we stay on page two, and we're able to carry on browsing our comments. Sweet.
Add regression test6:05
then we stay on page two, and we're able to carry on browsing our comments. Sweet. It would be nice if we could write some form of test for this, and we absolutely can. Let's go to our destroy test. First of all, we make sure we haven't broken anything by running the existing test suite, and when we're sure that we're absolutely fine, I'm going to find the redirect test that we already have, copy it, and paste it underneath.
I'm going to find the redirect test that we already have, copy it, and paste it underneath because we're going to make a slight alteration. So, we'll say it redirects to the Post show page with the page query parameter. And all we have to do to make sure this is the case, when we delete, we pass in the comment, and we'll set that comment like so, and we also pass in the page, which we'll set to two. And then when we redirect, well, we're going to assert that the post is set,
And then when we redirect, well, we're going to assert that the post is set, but we're also going to assert that the page is set to two. Let's run this. You can see that it passes. To be sure that that's not a false positive, I'll go back into the CommentController, and we'll comment this line out for a second and rerun. Now you can see it fails. So, we can be confident that that is going to work successfully,
Now you can see it fails. So, we can be confident that that is going to work successfully, and in the future, as we refactor, we can be sure that this functionality will continue to work, and we won't see this UI bug pop up again. Now, this is one of those things where you take a look at it, and you start thinking, oh, there's so many ways I could abstract something clever here, perhaps a piece of middleware that would automate that a little bit, or maybe on the front end,
perhaps a piece of middleware that would automate that a little bit, or maybe on the front end, I want to extract something that automates placing the page parameter in. Yeah, you absolutely could, and I've done so myself in the past many times. But just slow down. Oftentimes, you're overengineering. We've used it in one location so far. There is no point at this moment in time going ahead of ourselves too far and designing solutions for problems that just don't exist yet.
There is no point at this moment in time going ahead of ourselves too far and designing solutions for problems that just don't exist yet. This is pretty straightforward, it's clear, and it's easy to track down what is controlling that page number and update it as time goes on. So, look, I beg you, take things one step at a time. There's no need to refactor this to anything more than it is currently. If the need comes up in the future, that's when we'll perform the refactor. And because we have our tests in place,
Style delete button UI8:22
that's when we'll perform the refactor. And because we have our tests in place, it should be pretty straightforward. The last thing we'll do in this episode then is update our Delete button to use nicer styling. Let's come into the button itself, and maybe we'll set, I don't know, Font Mono. That would be quite cool. We could set Text Red. Maybe we'll make it 700.
We could set Text Red. Maybe we'll make it 700. How does that look? Okay, it's already starting to look good. We could make the text really small because that's not much of an issue. And I think MT2 would give just the right amount of spacing there. I want all the text to be on the right-hand side, which I could do here with Text Right. But I'm pretty sure if we post, like, a small comment, say, Hello World, yeah, look at that.
But I'm pretty sure if we post, like, a small Comment, say, Hello World, yeah, look at that. So we need to extend to the full width, and that's just because we're using a Flexbox up here. So on this div here, I could just say flex: 1, and now everything jumps to the far right. We need some form of hover state, so let's say, well, when you hover, we'll make the font semi-bold. And hopefully, if I refresh, yeah, there we go. Now when we hover, the font is semi-bold.
And hopefully, if I refresh, yeah, there we go. Now when we hover, the font is semi-bold. What about a Post where we don't control all the comments? Let's go to Posts, and we'll just choose something further down the line. Here we go. Doesn't look too bad, but there is obviously still that M2 gap underneath, which doesn't make sense because there's no actions. Tell you what, I'm going to use a little tailwind trick here. I'm going to come up to this div, and I'm saying,
Tell you what, I'm going to use a little tailwind trick here. I'm going to come up to this div, and I'm saying, if you are empty, then I want you to be hidden. So just disappear completely. Don't appear in the DOM, or rather, don't appear visually in the UI if you don't have any children. And obviously, if this vf is false, it won't have any children. And hopefully, yeah, you can see everything's just pulled up a little now, and we have even spacing on the top and bottom because there are no actions for comments that we don't control.
and we have even spacing on the top and bottom because there are no actions for comments that we don't control. But if I come in and post a comment myself, then we do have just that little bit of spacing between the Delete button and the tagline up here. Let's just click Delete once more to make sure everything's working. Yes, it is. Now, we're certainly not done with the feature set here for deleting. For example, it's way too easy to delete one of your comments at the moment. We should ideally pop open some confirmation modal
For example, it's way too easy to delete one of your comments at the moment. We should ideally pop open some confirmation modal that you have to confirm, but that in itself will be an episode or maybe even multiple episodes. So I'd like to come back to that in a couple of episodes' time because, first of all, I think it's important we focus on being able to edit a comment that you've already created. Let's tackle that in the next episode.
