Transcript
00:00 Let's start with our delete button. So we're going to go to the edit route. And right here, we want to have a delete button on each one of these images just right there. And we don't have an icon yet, so we're just using emojis. Icons will come later, no worries. All right, so what we're going to do is add a button right here. And inside of this button, we're going
00:19 to have a span for what we want the screen readers to hear, so delete image. And then the icon, or our fake icon, our emoji, for what we want users to visually see. And then to make things look nice, we've got this class name that we'll just stick right there.
00:38 And with that, we've got a button. And we don't have any action on that button or anything, but actually clicking on it does submit the form. In fact, it even will save and persist things. So we've got to talk about that. We'll make that work. That's going to cause us some problems, is all I'm saying.
00:59 We'll talk about that here in a little bit. But to make this actually remove things, we're going to say dot, dot, dot, list. This is going to come from Conform to React. That's a utility there. Dot, remove, remove, there we go. And the field set we want to remove it from is fields, images, name.
01:18 So that's the field set name. And then the index we want to remove is our image index. So let's grab index here. Now, it also would probably be useful for screen reader users to know which image they're actually removing, or at least the index. So we'll say index plus 1 right there.
01:38 All right, so if I now click on this, it, in fact, does get removed. So that's pretty cool. If I refresh, then it comes back, because that's the way that it is. So great, we've got the removal working. Now, to get addition working, we're going to do something pretty similar, actually.
01:56 I'm just going to copy this button right here. And we'll change this to be the plus image, which we want to show visually. And then we can just say add image for screen readers. But the utility we're going to use is totally different.
02:14 It's list.insert. So we want to insert an item in this list. And for this, we actually don't need to specify an index here. Let's wrap that up. There we go. We don't need to specify an index for this,
02:32 because the default will just be to append it at the end. But what we want to have appended needs to be a default object. So let's say default value is an empty object. That way, it's an object that's ready to have
02:48 a image associated to it and all of its properties and all that. OK, so we save that. And oh, man, that looks awful. So let's undo that, because we need, yeah, there's actually no class name. So let's get rid of that class name. And it'll just show up where it's supposed to go. And actually, it's not really styled very well.
03:08 We have a button here that comes pre-styled. So let's just use that component button. There we go. It looks a lot better. So now I can click on that. I add an image. I can delete these. And I can delete those images. But we're not totally done at all. In fact, we've got a bit of a problem. So if I say, I want to save this, I'm
03:26 going to say double exclamation point and hit Enter. Wait, what happened? I hit Enter again, Enter again. What? What in the world is going on? OK, so what's going on is these buttons are actually treated as Submit buttons. If we look at the DOM output for these and inspect that,
03:44 then we can come down here to that button. Here's that button right there. And you'll see that it has a name and a value. The name is double underscore intent, so kind of overloading that term. We use our own intents sometimes on our Submit buttons. But they've got a double underscore on here.
04:03 And then our value is like a serialized version that's like a path to the list that we're trying to modify here. And that is how that works to be able to communicate that this is the thing that we're trying to accomplish here. Why is it so weird? Why can't it just be like a React state
04:21 thing or something like that, and we just remove it from the array and all that? The reason is progressive enhancement. And so we need to have some server-side code that will be responsible for handling the case where the user clicks on this before the JavaScript finishes loading or something. So we're going to go into our action right here.
04:41 And we're going to say if the submission.intent is not equal to submit, then, and that's referring to this type of an intent right here. So that submission intent is going to come from conform. It's not going to be an intent that we put on our own buttons and stuff.
04:59 There's a little bit of overload of terms there, so my apologies there. But in that case, if we're not actually trying to submit the form, then we need to return early before we actually perform the rest of this stuff. And so what we're going to do is return a status of idle. So that's just like, it's not error. It's not success.
05:19 It's just idle. We're not doing anything actively. And then we'll send the submission back. And so conform is going to manage saying, oh, the submission type was this insert or this remove or whatever. And then it knows what to modify.
05:38 And so then what it will do is it actually modifies the submission server side and then sends that back to the UI, which will render that submission. And so even without JavaScript on the client, this should continue to work. So let's turn off JavaScript.
05:57 JavaScript is now blocked. And here, let's get rid of that. And if I hit the X, totally removed. Full page refresh and all that. We're getting full page refresh and still able to add and remove images on here, which I just think is pretty rad. And so if a user comes in and they're like, oh, I really need to delete this image.
06:17 It's like I posted the wrong image or something. Then they're going to come into their note. They're going to hit the X. And maybe they hit it before the JavaScript finishes loading. And I know that you have, I certainly have, tried to hit a button and nothing happened. And I'm like, oh, it's because JavaScript hasn't loaded yet. Regular users are also used to that. And that's a shame.
06:36 They shouldn't be used to things not working. And so you just wait for the JavaScript to load. And then you can press the button. Well, in this case, you don't have to wait for the JavaScript to load. You can hit it right away, and it will trigger a full page refresh. But it will work. And I think that is pretty phenomenal.
06:52 So that is cool and allows us to have this progressive enhancement stuff. But that doesn't actually solve the problem of me resubmitting this form over and over again. So the reason that's happening is
07:11 because when you hit Enter in an input, it's going to find the first button that's a Submit button. And it's going to use that for its submission. It's going to treat that as the button that was clicked on. Well, that is a problem for us because the first button is this Remove button. And then now the first button is this Plus Image button.
07:30 And so that's what's going on here. So the solution is actually pretty easy. It's a pretty straightforward solution. What we do is we just add a button right here that is type Submit. And yeah, the class is hidden. And then that's it.
07:50 So it's a hidden Submit button so that when the user hits Enter, this is the button that is going to be submitted. That's it. So now if I hit Hooray, and then we go back, Hooray, all of that is good. So a pretty simple solution, kind of a funny problem.
08:08 But I definitely really like having the progressive enhancement piece here without us really having to do a whole lot for that. So let's review what we've done so that we can be done. So first, we added the Delete button.
08:25 We're using the List Remove API from Conform. We're specifying which list we want to remove something from and the index that we want to remove. Then Conform will remove it for us. And then we're going to show to screen readers
08:40 or have in the DOM for screen readers, Delete Image and then the image number. And then for the visual aspect, we have this little fake icon emoji. And then we added a List Insert button here
08:57 that will insert onto this field image name. And this is the value that gets inserted, that default value, which is going to just be this empty object. So it's ready to take the image ID or the image itself and the alt text and all that. And yeah, then we show add images for screen readers
09:15 and plus image for the visual aspect. And then we went to our server side and we said if the submission intent is not submit, if they're not actually trying to submit the form, they're just trying to make some change to it or whatever, then let's return a status vital with the submission
09:34 so they can proceed with whatever they're trying to accomplish. And then we also solved the problem of submitting the wrong button when you hit Enter by adding this button type Submit with the class name hidden. So it doesn't show up visually. But as far as the DOM is concerned, that's the first Submit button of the form.
09:52 And so that's the one that will be taken. Now, of course, if we had our Submit button right here had a name and a value, then you would want to make sure you apply that name and value to this as well. So just another thing to consider there.
10:08 And that is complex forms with the add and remove. You should feel really proud of yourself because this is actually really cool what we're able to do here. So good job.