Hard Validation Problems
Today we talk about getting past some hard validation problems. I'm really happy with the result!
Transcript:
Okay. I'm standing here at my desk feeling pretty good for two reasons. The first reason is I solved a pretty hard validation problem in live wire. The second reason is, uh, it's, there's a giant blanket of snow outside and it's been snowing all day yesterday and all night really soft, nice, perfect white snow.
It's not even Thanksgiving. And we broke our rule and played Christmas music and actually decorated for Christmas because we were just feeling in the mood. Um. And it's just white outside, which is really nice. So thank you for, thank you for that snow. Oh, I solved a hard validation problem and I want to tell you about it.
So validation, it's been a long road to get where we are, and I thought we were at a good place with validation, but really we were at a good place for demoing validation. I'll explain what I mean by that. So in episode five, I, I just called the evolution of validation and I, I talk about how I went from going, all right, I live way or could not.
Handle validation at all. By validation, I mean like form validation, like showing this field is required and stuff. Um, forms are pretty common thing and in web apps and so validation's also pretty common and live wire could, there's, this was sort of a fork in the road. I could not deal with validation at all.
Um, but I wanted, I wanted live wire to support. Some, you know, making validation easier. So what you could do without any, you know, anything official Livewire you could, like at the, the olden days in Laravel, you could create, you can manually create a validator with rules and data, um, and pull it from wherever, from your Livewire data, and then you could run validator Aero validate.
Or you could run fails, probably put fails in a conditional and then manually, you know, build up, uh, some data for the errors and show the errors and yada, yada. But I wanted it to feel native. I wanted you to feel like you were in a controller method and you could just do. This arrow validate, um, which would be the equivalent of request arrow validate.
So you could just do this arrow, validate pass in some rules and it would Intuit, it would like link up the rules with the data on the live wire object. And so if you use live where you've probably used validation and you know what I'm talking about, it feels really sleek. It feels really intuitive.
And I already talked about how it was a long road to get to that point of it feeling intuitive. Um, that's episode five. So since then. Uh, so the, it feels really good and it looks really good for demos, but in reality, there's a problem with it. So the problem is I implemented it in such a way.
That it's pretty simple. Like you run this arrow validate and actually, you know, does what layer of L does inside and throws a validation exception. And it bubbles up. And I have a little catcher and I catch the validation exception and I pass the errors into the view. So you have an error message bag, so your execution gets interrupted, but your components still renders and you get that errors, message bag, you know, money sign errors, probably arrow has or arrow any or arrow GAT or something like that.
Um. So that that's the way it exists currently. The problem with that is the errors aren't persisted from request to request. So you have a form and you submit it, and then let's say you have some validation in the submit method or something, and then the validation errors are shown as soon as Livewire makes any sort of update and probably your, your data binding, your input elements.
So if you have an input element called name, all right, let's start concrete. You have a form with a name and an email. No username and password and the username field is just an input type text, passwords, input type password. They both have wire, colon model username and password, and their data bound to the Livewire component.
So every time you type into him. They sends an Ajax requests and updates the data on the component. Then you have a submit button that does wire colon click a submit method. In the submit method, you have this arrow validate and you validate that user name and password are required, let's say.
Okay, so the user's typing in, let's say they don't type anything in and they just hit the submit button. They see this fields required, and then they see this fields required the other field. So they type in the username field and now Livewire sends an Ajax request to update that field and all the validation is just wiped.
So all the validation messages disappear, but they only updated one field. Now what you'd rather have. Now that's not, that's not horrible, but the default behavior for like without Livewire would be, you would type and the validation messages wouldn't disappear until you hit the submit button again.
Right. And let's say that you, that you implemented a few JS. Handling of this so you could real time validate and like sundae Jack's requests or whatever, realtime validate it would validate only the field and the validation messages and the other fields wouldn't update unless you updated those field values.
Very hard to explain. Um, but I hope you're kind of tracking then it's just kind of this, this weird problem that at first felt, it just felt a little weird to me. Like, okay, this is the easiest solution, I'm going to do it, but it doesn't solve all the problems. And then when I would think about a way of handling validation, like maybe had just persist validation from requests or requests, it would break another use case.
Um, so I just kind of left it and there's, there've been get hub issues submitted about this, and it's kind of jarring. Like people don't expect that behavior. They're like, validation messages disappear after update, you know? And I'm like, yeah, that's actually how it's supposed to work. But I know it's not ideal.
So what we really did. Uh, so we, I started this big discussion in an issue that somebody submitted to, lots of people have been kind of weighing in on it. And it took me a little bit to figure out, but really like, there's two different kinds of validates. There's probably more than that, but there's two primary primary types of validation in a, in a web app.
But let's just say in Livewire, there's form validation. So they're like, honest submit, the kind of validation that that happens every time you hit the button. That does the thing. And then there's realtime validation, the kind of thing that might, is probably more common on an auto saving form.
Um, but even on a, even on a normal like button forms that there's real time validation, they both behave differently with the button you want. Um, you want all the errors to persist, uh, across requests. You want them to sit there, um, with the, with the realtime validation, you want them to update but only update the field.
So like I explained, it's a little bit hard to visualize, but you can imagine that there's these big differences. So basically I had to sit down and realize that, okay, the first thing I need to do is figure out how I'm going to persist validation from requests or request. So like I talked about in a couple episodes before that, uh, I think a lovely refactor was the, um, was the episode.
And it's, it's where I refactored LA live lawyers core. To basically the the back end core to do this hydrate dehydrate middleware stack where like the request comes in and I slowly build up the Livewire component from the request with these different middleware slices. And then when the request goes back out, before it goes back out, ID hydrate the instance into a response.
And then JavaScript stores, it passes it back, yada yada. So this was kind of nice cause all I had to do was add one piece of middleware called, um, I think I called it persist validation. I don't know, persistent errors, maybe I called it. And so basically I do that before I D hydrate the component.
I see. Hey, um, whenever, I don't have to tell you in line by line what I did. But basically each component keeps track of an error bag. Now, um, if you don't know how, how level validation works, it's kind of interesting. You've probably encountered message bags before and Laravel but you probably, you know, it's kind of a nebulous thing to you.
Like you kind of know that the error bag has a message bag and a message bag is like a broader concept. But, um. And that's how you get that nice like error errors. Arrow has errors, arrow, any, right? Um, but anyway, so live where now it keeps track of a message bag and there's lots of weird complicated things it has to do.
Um, but I'll just give you the gist because we're already eight minutes at eight minutes, and hopefully I described the problem enough that you can understand that it's an annoying problem. It's hard to solve, but. I finally solved it. And, um, uh, okay. So instead of going out about the implementation, I'll tell you the, how you use it effectively.
In that previous example where we had username and password and then that submit button when you hit that submit button. Now the validation error show, but when you update a field, they won't disappear. They'll stay stagnant. This, this is the exact behavior, um, of a traditional form submission where when you hit submit and it runs some validation, the validation is static.
So as you're typing in username and you already had the username field is required message, it's still showing while you're typing in. Then you click the button again and it reevaluates the validation. If it passes, they're all cleared and you do whatever you do. If it fails, you get the new validation messages.
Okay? That's use case number one. Use case number two, and this is the thing that I've been banging my head against the wall for so long and I realized I wa I can't use the same, this arrow validate. So I have a new method called this arrow validate only. And this is presumably you're doing real time validation, and if you're doing that, you're doing it in an updated hook.
So Livewire has these updated hooks which run every time you try to update a piece of input. So let's say you have your updated hook. Or updated username or whatever updated hook that gets the value that's being updated. You do this arrow validate only, and then you pass in the field. That's the name of the field.
You pass in all the rules after that. Um, and then it's smart. It validates only that field and it merges the airbag with the old Arabic. So as you type in, you get validation messages, but you don't get messages for the other fields until you start typing into them. And when you type into them, it doesn't clear the other validation message that exists.
It merges with them. So they still exist. So basically it's a way of doing validation where you're only validating one field in one field only, but the other ones persist. Hopefully this makes sense. I have like five seconds left. Coming up with the name validate only was hard. That was a whole other story.
I'm really happy with it. I thought about like validate realtime, blah, blah, blah. We're at 10 minutes. Thanks for listening. Hope you enjoy this validation. We'll be tagging the next release. Bye.