Validate as You Go
Hi, my name is Jean- and I’m going to go through a customer form, start removing required field on the employee higher date, not a lot to be in the future. Or maybe you get into something where, yeah, you might validate that they have to have these two required fields, but you might also validate that two fields cannot be in conflict, something like this. So, yeah, that’s pretty much it. Let’s go ahead and dive into the code, but before we do that, just a reminder, we’re doing a five-part series on what’s new in Servoy8.1. It’s Thursday, part four or five. As you all said, we’re posting these on the website, the recordings plus the slides and the sample solutions. So, there’s a new form event in 8.1. It’s called on element data change, and it’s a form level event. And you might already be familiar with on data change for an element. So, this is really the same thing, but it’s called for every element on the form. And similar to the, there’s the form focus events, which work in the same way. There’s an event on the component, but also on the form. So, it’s really the main reason that we came up with this as part of a solution to validation is, well, I mean, we had a lot of discussion about all the different ways that you can do validation and how we can help with that. And as you know, with Servoy8, we like to keep it very open so you can implement kind of the user experience that you want. So, for us to decide how validation should work from a top down approach kind of goes against how we do things. So, we came up with this, at least for starters, which is just a really simple addition that makes sort of setting up a validation framework a lot easier to do. And so, you don’t have to hook up data change to every, every form component. You can just hook it up to a form. You can use it in combination with form inheritance and some CSS to deliver a great user experience. And it keeps that sort of separation of UI and business logic that, and VC approach that we like. Well, enough about that. Let’s get behind the demo, look under the hood. So, let’s start with, say, our order form here. You can see that we’ve implemented the on data change event that is new. You can find it in the properties of the form. And, actually, what I did in this case was I created a base form called validation base. And you can see that the extends property points to that. So, really, there’s nothing on this form, except for the error balloon that I’m showing. the event binding is already set up. And so, really, what we do is we call, we handle this event and we call a validate method. And so, that validate method gets called frequently. And it just allows the UI to always catch up to what the state of the model is, say, the found set or the record or even form variables. And really, just using it as a trick to frequently update the UI. So, it’s always in sync with how the model is. We’re not waiting until save or some other later event. This validate method is just basically it passes off control to some other handler. And this is just as Yawn mentioned the best practice. Sometimes, we see a lot of folks putting a bunch of their core business logic inside of form scopes. And it’s convenient to do that because that’s the starting point. And then also, if you want to update the UI as you go, then, you know, it’s also convenient for that. But it’s really makes for poor architecture. So, in this case, I’m looking for, I’ve written all my business logic in an entity scope. So, it has nothing to do with the UI. It might even be in a separate module that we include. And it’s a function called validate and it passes back basically a list of errors with some associations to a data provider that might be an error. So, I don’t know if the found said will have it or not. So, if it does, then I call it if not then nothing happens. But for our customers table. You can see in the table editor, I have some method implemented here called validate. And really what this does is well for the customer entity, it just iterates over a list of required fields and it attaches sort of a validation marker that I call it, which is really just consists of the data provider ID that’s in error. And a message and that could be an I18N message or something. So, it would be ideal to externalize it. It’s also pooling the title property of the column. So, if you put an I18N message there as well, then, then grab that. So, it’s good to keep all that stuff separate. So, in this case, we’re just doing the required fields. And that’s a pretty simple example. But one of the reasons that this has been a sort of a continuous topic for us is to have a sort of a continuous topic for us is that we see that people do validation in all different ways because they have all different sort of business needs. Often, it’s not really validating the field itself, but you’re validating the whole record or even just the whole model. It could be related records and something like that. For example, you can’t have a child record unless a certain field is set on the parent record, something like that. So, that’s why we’re doing this sort of on the found set itself and not just, you know, hey, this is a required field or the email that you entered is not the right format. So, another example of that was on the orders table. And you can see that again under the methods, we have the validate method. And in this case, we added a little bit more logic to just over and above required fields. We wanted to make sure that the ship gate couldn’t be before the the order date. That would make no sense. In that case, I’m actually attaching two markers because it’s a problem with both the ship date and the order date. This is just an example implementation. You may do it differently. So, let’s take a look at what happens in the UI. You’ll notice in the in the demo when I changed the this both fields become highlighted. There’s an error balloon sort of out to the right. And then if I clear the problem, it goes away. You’ll also notice that as I’m working, it’s actually appending the errors and moving that that balloon around as well. So, there’s some activity going on in the in the UI to match up with what’s happening in the model. If we go back to our base form, then you can see. Really what we do is we get from those those entity methods, we get just an array of markers and array of these sort of errors and associations to which data provider IDs might be an error. And then we update the UI, which is another method. There might be a bunch of other stuff in here too, not related to validation. But in this case, it just it just has validation related to why stuff. And so really what we’re doing is we’re iterating over each of the fields. And we’re checking to see if if there’s an error marker associated with that field. I put that in a little subroutine down here that just checks the markers. It’s pretty straightforward. And then this goes back to one of the earlier topics this week, which is the ability to programmatically add and remove style classes. So I have a CSS style class setup called invalid. And I’m just going to add it to the field if if it did have a marker associated with it. So that’s another nice form or separation that we’re out then. You know, changing the background color to wrap and just say, well, it has the style class. And if I want to make a global change, then I can just change the style class in one place. And I’m also adding a tool tip text which carries the message. I don’t think I showed that here, but when we have the validation error, also have a tool tip there that gets set and it gets cleared. Finally, we we want to move that call out bubble. So I was I was programmatically moving it could also be done with CSS. That might actually be a better way to do it. You could do it relative to another component. And I’m sort of pushing the messages into that variable that’s sort of being reflected in that that bubble. If there are no markers, then I’m actually with a field I’m actually removing the style class in case it was added. So you’ll notice that when I when I fix the problem that the markers go away. So that’s really pretty much the UI side of it. I did want to point out that this is happening in form inheritance. So it’s it’s really setting up kind of a framework for how how this could work. And that’s how I was able to quickly do this on say three forms. And I thought it might be worth showing you how to do it on on a new form. So I’m going to create a new form. We’ll base it off of the products table. And put. Put some fields on the form. Okay. All right. Now one thing I’m also going to do is I’m going to set that this form extends my validation base form. That sets up the some of the UI stuff that we saw and the event binding to. To the on element data change event, you can see that it’s it’s already bound up here. And I’m going to go to the main form that I had. And also put it in our tab panel so that it shows up. There’s one more step that I need to do actually hook up some validation logic. So I would go into my product. And it’s looking for that method validate. And some markers. And let’s put a little bit of code in there. So we’re very like. If the cello elected record in the units. Units in stock is less than zero say. Then we’ll add a marker. And I think it would be called data provider ID is units in stock. And the message is. I’m not being negative something like that. That’s the gist of it. So now we have some some business logic. It’s totally separate. We could call this having nothing to do with the UI. The UI is handled in the base form. That’s the way we like it. Let’s try it out and see if I made any mistakes. Here’s our. I don’t know how it came in that it was. Table view. Must have accidentally done that. So now we take our units in stock. Down to minus number. And we get our validation hand out there. So that’s kind of the gist of it.