Conf42 JavaScript 2020 - Online

- premiere 5PM GMT

It can't be this easy. Form validations with Vest

Video size:

Abstract

Vest is a framework for form validation inspired by style and syntax of unit testing framework. It gives you a declarative structure to express your form validations, separated from your feature - allowing you to write your validations faster, with more confidence and without making changes to your feature logic.

We will take a deep dive and together implement Vest in an existing form, and learn how to use some of its most useful features.

Summary

  • Vest is a library intended for use with form or input validations in JS apps. Vesp tries to suggest a structure for formulation that's also separate from a feature code. It supports most of the features you would expect from a validation library.
  • Vest lets you integrate vest into an existing form and an existing feature without all the hassle. Here's how to create a validation suite from scratch. Inside our input component, it's a simple react component.
  • Vest only gives us a way to only validate a specific field and which field we want to validate. Let's see now, this is kind of a weak password. We want to suggest the user to add a number to make the password stronger. But I cant it to be orange. I don't want it to fail form validations.
  • Vest supports async validations. What if we wanted to test that the username is already present or already taken? Vest gives us a way to pick into the uncompleted validation to the intermediate validation result and get validations that already failed or passed.

Transcript

This transcript was autogenerated. To make changes, submit a PR.
Hey, what's up? I'm Eva Tar. I'm a front engineer at Facebook here in Tel Aviv, and I'm here to talk to you about Vest, which is an open source framework I'm very excited about. But before I begin, I just have to say, Vest is not a Facebook project and it is not affiliate with Facebook. Just have to get this out of the way. Vest is short form validations tests. So as you can probably guess, Vest is a library intended for use with form or input validations in JS apps. And if you've done even a little form validations in the past couple of years, you're probably familiar with the amount of form validation libraries that occupy this space, and it's only logical to ask, why create a new one? And that's a very valid point that I usually tend to agree with. But in this case, I think we can only answer this question if we actually cover the problem and describe the way form validations works today. So in a typical scenario, what we usually have is a user that goes to a website, they face a form, let's say user login form, and they type inside an input box, and then event handler gets triggered. This event handler usually calls a function that we wrote or some third party library that contains a validation code. But this function usually takes care of the validation. It decides whether the inserted input is valid or invalid, and then it's up to us to decide how to show that to the user with a red x or a green checkmark or whatever. Now, this usually works if we only have only a limited amount of fields in our form, or if we do not plan on expanding and adding more features to the form in the future. Because from my experience, as long as our feature grows and the more complex our feature gets, the more our form gets more complicated and the validation code gets more complicated, because what we usually tend to do without any specific structure for form validations is put the validation code inside our change handler, or inside our component, or inside our feature, and then we do not have a way for describing it separately. It becomes a part of a spaghetti code that's called our feature, and we have no easy way to test the validation code, we do not have an easy way to maintain it, and it's very hard to read, very hard to write. And if we want to do, to create a refactor in our feature, we have to basically scrap our validations away and rewrite them from scratch. And this is what Vesp tries to solve, and this is what Vesp comes to handle. Vesp tries to come and suggest a structure for formulation that's also separate from a feature code. And the way Vesp does it is very easily by borrowing the syntax and style of unit testing frameworks such as mocha or jest, and implementing it in the world of form validations. So basically you describe a form validations suite that contains each and every test or each and every field in your validation suite, and then it can easily separate it from your feature code. It has a very narrow point of contact with your code, and it supports most of the features you would expect from a validation library. And actually I think a little more now, I could talk about vest all day, but I think it would be best by showing to you through code. So what I want to do now is show you how easy it is to integrate vest into an existing form and an existing feature without all the hassle. And what we're going to do is take this form, which is a react form, and it doesn't matter at all that it's a react form. It could be angular or view, or even without a framework at all, and add some validations to it from scratch. So it doesn't have any validations at the moment. We're going to install vest to our sandbox and create a new file, and I'll call it validate js. The name doesn't matter. I'll import vest from vest, and I'll create my validation suite and export it, because we have to run it from our form. So export default vest create. That's how we create a new validation suite, and we'll name it user form. The name doesn't really matter. And I'll define my validation suite body, and we get form validations, the data to validate from our form through the arguments. So what I do here is data, and I'll initialize it with an empty object in case it doesn't get sent. Now we have to write our test for form validations, just like in unit test, we have a test function we'll import test, and we'll also import enforce, which is pretty much like expect. This is our assertion function enforce. And I will do a dummy test just to see this stuff works. So I'll do test, and the first argument is the name of the field we're validating. So I'll do username. And now the second argument is not a function like in unitest, but instead it's the string of the message that we want the user to see in case of validation failure. So here it's going to be like for example, username is required. So username is required. Okay. And in the test body I'm going to do enforce data username. So enforce username is not empty. So this is our first test ever. I'm going to go to the form and I'm going to connect it. So the first thing I'm going to do is I'll import my validation test, my validation suite, sorry, import v form validations valid. Oh, I called valid. Okay, I'll rename the file valid. Okay, I'll import v from validate. And what v gives us is basically a function that when we run it, our validation suite will run. So I'll use it inside our change handler. And what we have in the change handler is basically a state setter and it sets the states to the key value pairs of our fields in our form. So it's going to be for example current field, so it's username and the value we put inside. So that's how our state looks. I'll do v and I'll pass next state to it as well. Now if we run it, nothing will happen even though the validation will run. What we have to do now is display the validation result. So what we can do here is constraint equals v get and this is how we get the validation result from that v object. So we take the result and inside our input component and it's a simple react component, it has nothing to do with best. I already added a validation messages prop that takes an array of strings and displays the first one as the validation message. So it's going to look like form validations messages and it's going to take an array of strings and this is the error we're seeing here. So what I'm going to do now is instead of passing a hard coded array, I'm going to res get errors, username, the name code we're validating. And what we're going to see now is that when I remove the stuff that's written here, we're going to see username is required because that's what we stated here. Now it's not in red and I already have a class name in that input for validation errors. So it's going to be like this. And we can do this automatically with the best helper utility. We can import class names from best class names, import class names from best class names and what it does it allowing us to specify which class names we want to show in case of form validations success or failure. So in our case let's do it like this, it returns us a function. So we'll name this function Cn. For example, const Cn equals class names. It takes form validations result object so res as the first argument and an object of key value pairs with the names of the classes we want to show. So for example, in case the form is invalid, let's do error. Sorry. In case it's valid, let's do success. In case it's invalid, let's do error. And in case it's warning there is such a thing, let's do warning. Now we take this again and put it inside our input form and do class names. Class name, sorry. And do cn username. And what we'll see now is that it turns to green because it's successful. And if we remove everything from it, it's empty. So it's red. So this was easy. Let's copy it over to the rest of the fields in our form. So username password let's also do this for firm and in terms of service. I don't want to actually have a validation message because it already has some text. Let's just add the class name for the color. So let's do tos. Now let's quickly add some more validations for our username. So let's do username must be at least three characters. Okay, longer than or equals three. Now let's copy both these for password and password must be at least five characters. In this case let's do just longer than four. Okay, username a must be at least three characters. Password is required. Abcde. Okay, perfect. Now let's do confirm test confirm passwords do not match. Let's do enforce data confirm equals data password password now let's see. Abcde. Wait, data confirm data password. Why not? Oh, I have a typo. Here it now let's do the same for terms of service. Test tos. And as I said, we do not need a validation message here. Let's just do enforce data tos is truthy because it's a boolean value. In our case it's checkbox perfect. Now one thing I'm seeing in the confirmation box is that when I remove the password and then I remove the confirmation, it's green because they are equal, but they shouldn't be green I think because it's empty. So let's fix this if data password. So only if data password is present. Let's run the confirmation. So let's try this. Perfect. It's empty. Now it doesn't have any color on it so it's not getting validated at all if password is empty. But there is one more thing. I type inside username, for example, and all the others get lit up. We're basically validating all the fields, even though we're only touching one of them, which is not a great user experience in my opinion, because the user only touched the first one, it's obvious that the others won't be filled yet. So let's fix this. Let's do vest only, which gives us a way to only validate a specific field and which field we want to validate to validate the field that the user is currently touching. So what I want to do is get somehow the current field and pass it over to only. And here in our change handler, we actually do get the current field. And again, this is a simple react handler. I'm taking this and I'm passing it over to vest. And what I'm getting now is that only the field that I'm touching is getting validated. The rest aren't cool. Let's see now, this is kind of a weak password. Let's say we want to also suggest the user to add a number to make the password stronger. We do not want to fail the validation on this condition, but we want to suggest it. How would you do that? With vest you have the worm optionality, so it's going to look like this. Test password again and password is Wic, maybe add a number. Okay, and now let's do enforce data password matches and then we can pass a regex. So let's do a simple number regex. And what we're going to see here is that we're getting that validation message. But I cant it to be orange. I want it to be a warning. I don't want it to fail form validations. So what I can do here is tell vest to put it in a different basket using vest warn. So vest warn. Vest warn. And if I try it again, we orange. We're not seeing form validations message because back in the form we asked only for the validation errors here. If you see here get errors. So let's also put the warnings inside. So it takes an array. So we can either pass the errors array or the warnings of array. In this case I'll just spread both those for a single array and we're getting it perfect. Now what about async validations? For example, what if we wanted to test that the username is already present or already taken? Sorry, we wanted to go to the server and see if a user is already taken. This supports async validations. In our case I already have a server call. It's a dummy server call import from API I have does user exist? It's a mock server call. And what it does is basically returns a promise that rejects when the user exists and it resolves when the user does not exist. So what I'm going to do here is write a new test for username. Again, username already taken. And what this does is basically similar to how does user exist. It fails in async validation if a promise returned rejects. So if I have a promise here and it rejects, the validation will fail. So data username. And now if I try typing something inside, for example a username that I know is taken. We won't see anything because form validations is async. Might take a couple of seconds and we're not rerendering react here once the validation is complete. So what I want to do instead is something different. I first want to display a spinner. So I have a spinner prop on my input. It's called pending. And what I want to do first is when I make a change to the username field just to show that spinner. So I'll create a new state for react. Cant is pending, set is pending equals use state and default it false. Okay, so if the current field is username, I want to show that spinner oh, set is pending to true. Perfect. And here I can already decide to show pending by is pending. So if I type something inside, we're saying the spinner cool. Now what I want to do is wait for the username validation to complete and then disable that spinner. So I'm going to take form validations result and what I'm getting here from the function invocation is the validation result the same one that I'm getting here in res. So what I'm going to do is const res equals v and then res. And what I have here in my validation result is a function called done, which basically takes a callback and runs it whenever the validation is fully complete, all the async validations as well. So what I'm going to do here is pass a callback that all it does is sets is pending to false. So what we're going to see here, for example Abc, and now we can see that the validation is complete because the spinner is gone. Now what happens if I use a username that's already taken? That's all there is to it. That's it, we're finished with the async validation. But I do see something else here. So if I try typing inside an invalid username, for example AEA, we're seeing the spinner, we're going out to the server even though we know that the validation already failed for the username, the username is not valid to begin with. We shouldn't go to the server in this case. So vest gives us a way to pick into the uncompleted validation to the intermediate validation result and get validations that already failed or passed. So what we have here is best draft, and what gives us is the same validation result, only uncompleted. And what we have here is has errors for username, and this returns a boolean. In our case it will be true because the username is too short. So only if the username doesn't have any errors on it, we'll go out to the server. So let's try again. Ea, we're not seeing the spinner because we're not going out to the server. Okay, let's try again. And the username is valid technically. So we're going to the server. But another thing here is that I'm going to the server again when I'm fixing it, and if I remove it and go back to the username that was there before, which is invalid, will still go to the server, which is not a good idea because we already know it's invalid, we already know it's taken on the server. Vest allows us to memorize validation results using test memo, and we can rely on a dependency array and specify which values we want to memorize by. So I want to memorize by the username, which means that if the username doesn't change, or if we get the same username again, we shouldn't go out to the server at all. We will just use the same validation result that we had before. So let's try again. Data username. So what we're doing here, we're testing memo for the field username only if the username is not changed. Otherwise we're getting the same validation results that we already got before. So let's try again. We're going to the server the first time, we're going to serve it again, and we're not going to the server third time because we already know the user's name is taken. So what we have here is basically this is all our form validations. It's separated from our features logic. It took us about, I think, five lines of logic to add vest to our features, and a few more lines to just display it. So as you've seen, vest is a pretty powerful and a pretty robust framework. It has lots of features and capabilities and we did not even cover all of those today. And yet it still maintains all the complexity in different modules and has a very narrow point of contact with your code. So you can make refactors or change your feature or even replace your UI framework. You cant even touch form validations code and worry breaking I'm a big believer in the value vest gives you as a developer to build your app faster with more confidence. I've used vest personally in multiple production apps, others have too, and I've received some positive feedback about Vest. Now I'm looking for more developers to come and help me shape the future of vest. And I'm also looking for more community adoption and people to come and suggest new feature ideas to vest. If you have any any question, you can ask me at Twitter or go to the documentation page on that projects GitHub I'm very much excited about best. I hope you like it too. Thank you.
...

Alush Evyatar

Front End Developer @ Facebook

Alush Evyatar's LinkedIn account Alush Evyatar's twitter account



Awesome tech events for

Priority access to all content

Video hallway track

Community chat

Exclusive promotions and giveaways