Video Summary and Transcription
Today's Talk focused on building forms in Vue using FormKit. The speaker highlighted the simplicity of forms in Vue and the importance of adding buttons, labels, and help text for a better user experience. They also discussed handling form data and errors, refactoring form components, and implementing inline validation. The introduction to FormKit showcased its features such as a single component approach, automatic data collection, and simplified form building. The talk also covered applying validation and form generation using the FormKit schema, which allows for easy form representation and rendering.
1. Introduction to Conquering Forms in Vue
Hi Vue.js Live. Today we'll learn about making forms in Vue with a focus on FormKit. I'm Justin Schrader, involved in projects like FormKit, Arrow.js, AutoAnimate, and Vue Formulate. Building complex forms can be tedious and hard to maintain. However, forms are easy. All you need is a form tag and a couple of input tags.
Hi Vue.js Live. I'm talking to you today about conquering forms in Vue. We're going to learn about how easy and wonderful it is to make forms in Vue with a particular bent on FormKit.
So I'm Justin Schrader and you might know me from the internet, you might not. Some of the open source projects that I'm involved in are FormKit, which is what we're talking about today. Arrow.js, which is a small two kilobyte lightweight alternative to things like React and Vue. Another one is AutoAnimate, which is really neat. Works great with Vue and React and really any other ones. And it lets you automatically animate DOM elements coming in and out of the DOM or moving around. And then Vue Formulate, which is the spiritual ancestor of FormKit. It was a Vue 2 library, all about building forms in Vue. And finally, I'm a partner in an agency called Braid here in Charlottesville, Virginia. So that's me.
This is also me in 2019 staring at a massive client project that we had built with hundreds, maybe even thousands of inputs and forms. It was incredibly tedious to build, very hard to maintain. And at the time we were thinking there's just gotta be a better way. So let's talk a little bit about that because the prevailing wisdom on the internet right now, especially in dev Twitter is forms are not hard. Forms are easy. In fact, Ryan Florence, one of the creators of Remix recently had this tweet and I think it applies, you know, he's talking about remix and react, but I think it applies to really any of the big libraries out there. Let me read this. I don't think there should even be form abstractions. Forms are fundamentally markup user interactions, event handlers, state management requests between views fell to react. What will the form library out here? Toss in some validation and be done with it. Interesting sentiment. And you know what? Fundamentally Ryan's right. Forms are easy. All you really need is a form tag and a couple of input tags and voila, you've got yourself a completely valid form. Um, here's one. It's a username and password. This could be a login or registration.
2. Adding Buttons, Labels, and Help Text
It's incredibly simple. We should put a button for submitting the form. We need labels for accessibility. Adding help text makes the form look better. Now we're ready to accept and process the data.
And as you can see, it's incredibly simple. Now to be fair, we probably should put a button in there for submitting the form. Um, in this case we put a register button in there. So this must be a registration form.
And realistically for accessibility reasons, we're going to need some labels on those. Plus people won't even know what to fill out if we don't put them on there. So let's go ahead and do that. We'll just add an ID to our inputs because we need to semantically link them with the four attributes. So we'll just pop that on there. Great. Look at our form renders. People would know how to fill this out. We are good to go.
Although to be fair, it does look a little bit like a login form, so we should probably add some help text on those inputs. And to do that, all we got to do is add an ARIA described by on our actual input. And then wherever we put our help text, we can put an ID, uh, something like, you know, username help. And now we're good. Now we're off to the races. Here we go. Now our form is starting to look better. We've got help text, you know, could probably use some styles, but it's, but it's looking good. Forms are easy.
To be fair, this doesn't actually do anything yet. So let's go ahead and put a script set up in the head here, and we'll just put an at submit. This is view. Super easy. Submit handler. And now we're ready to actually accept this data and do something with it. And there's a couple ways. We can get the data right.
3. Handling Form Data and Errors
One way to handle form data in Vue is by using a reactive object and the V-model directive. We can store errors reactively and display them next to the corresponding input fields. This approach allows users to see and fix errors without having to remember them. It provides more control over form inputs and the ability to handle errors from the backend.
One way would be if we added an ID on the form, in this case, ID register, and then all we need to do is pass that element to form data, and it's going to do some collection for us and get some data out of our form.
Now that does work. Although in view we like to sort of have a little bit more control over what's going on with the inputs. So realistically, we're probably going to use a reactive object with maybe our username and password as properties inside of it, and then we'll just V model will be model formed data dot username will be model, the password, and then we'll pass all of that data to, you know, whatever function is going to actually submit it to the API.
So this works. Um, I don't see any problem with it. Well, there's one little thing to be fair. When we call submit to API, we don't know what's going to happen. There could be errors. So we probably need to handle errors coming from our backend, but you know what? No big deal. We're engineers. We're up for the task. We're going to get it done.
So let's take a look here. Um, all we really need to do is have some way to store our errors reactively. Something like a ref. Now we've got our errors here, and then we'll put a try catcher on our submit to API and we'll just assign the result of those errors to errors value. And then somewhere down inside of our form, maybe at the bottom, close to the close to where the person clicked the submit. We will have something like a VF errors and we'll just render all of the errors right there at the bottom. No problem. What's this here? Ah, Nielsen Norman group. Very respected. Let's uh, let's read this. Keeping error messages next to the fields in error minimizes the memory workload. Users can see the error messages while fixing the error instead of having to remember it. Got it. So they don't want all the errors at the bottom of the form. They would like them placed where the actual inputs are. Cool. Cool.
4. Refactoring Form Components
We can extract some parts of the form into separate components. Here's a new component for the inputs, accepting props like label name, help, and errors. We need to handle the form data differently, adding a model value and updating it through input events.
Cool. No problem. Uh, we can figure that out. Now we probably at this point, to be fair, this form is starting to look a little bit ugly. We should probably like wrap some of these, you know, extract them into components like this area right here, for example, would be much better if it was written as an actual separate component.
So here we go. Here's a new component for our actual inputs. We've got defined props. Um, we're gonna accept a label name, help. And now we're gonna accept the errors so that we can place them with the input. And then down below here, the only thing that I see, you know, most of this is coming in or now rendering errors, but we do still have this V model. So that is a problem, because, of course, form data is no longer in our scope here. So what do we need to do? Well, we need to add model value and then something like define a mitts for the for updating the model value. And then instead of doing a V model, now we need to listen to the input events on the input, and we need to update an event called update model value with our new value. Good to go.
5. Inline Validation and Form Abstractions
Ideally, all validation should be in line. We need to add in line validation rules on the inputs. We need to figure out how to aggregate the validation and know if the form is valid. We also need loading feedback and styles. Ryan Florence's tweet suggests that form abstractions may not be necessary as forms are fundamentally markup and user interactions.
Hmm. What's this one? I got another one. Ideally, all validation should be in line. That is as soon as the user has finished filling in a field, an indicator should appear nearby if the field contains an error.
Okay. Mm. Validation. Inline validation. Okay. Before I rush in and keep trying to make these iterations, let's figure out a quick list of what needs to be done here. So we need to add in line validation rules somehow on the inputs themselves, I guess. Um, then we need to figure out how to get the validation from down inside of those inputs and aggregate them so that we know whether or not our form is valid before it gets submitted.
Frustrating. But we could do that. And then we need to probably have some sort of loading feedback. I would imagine. I mean, what happens if we have to nest these a little bit deeper than we have to pass all of those props down through other components? We need to add styles. This form is only two inputs, literally two inputs. And the complexity is getting out of hand and it still looks like garbage.
So it brings up the question forms are easy. Let's re-examine this hypothesis. Okay. So here's Ryan Florence's tweet again. I don't think there should even be form abstractions. Forms are fundamentally markup user interactions, event handlers, state management requests between views, Felton react. What will a form library at here toss in some validation functions and be done with it. Interesting. I think what Ryan is trying to say is that if you think of the two domains that need to be handled by a form in a framework that they are that the overlap between them doesn't look like this. I think Ryan's saying that people out there building form libraries have an overlap in their minds that looks something like this, like a little bit of what the framework does is a little bit of what the form does. And what Ryan saying is actually the Venn diagram is completely overlapped.
6. Introduction to FormKit
Forms are easy, but building them can be tedious and time-consuming. That's why we created FormKit, a form building framework specifically designed to simplify the process. Unlike a UI library, FormKit focuses on the architecture of your form, including validation and other features. In this talk, we'll cover four key aspects of FormKit: the choice of a single component, automatic input value collection, applying validation with a single prop, and form generation. Let's start with the single component, which is equivalent to the HTML input tag and applicable to all input types in FormKit.
Everything that forms do frameworks do, and with that, I agree. The only difference is I believe that the domain of the form looks a little bit more like this. It is a dramatic amount of additional work on top of what the framework is doing. So it brings up the question, form framework. Forms are easy. Huh? Well, here's what I'm saying. If forms are just markup user interaction, event handles, state management requests, and they still suck to build, then they deserve their own framework. This is what we're spending all of our time doing as engineers, at least a significant portion of it. Maybe we can make it better. So that is why we created form kit, and that's why I'm excited about it and passionate about it. Form kit is specifically a form building framework. It's like an entire framework specifically for this one use case. It is not a UI library. So form kit is much more about the architecture of your form, making sure that all the validation and everything else is all put together rather than just being like a component library. Let me show you what I mean. So when I sat down and was thinking about all of the features that are in form kit and I wanted to make sure that we covered them all, I just decided to start panning them out, kind of make a dirty list of them all. And I didn't even really finish before I realized this is just too ridiculous to fit into a 20-minute talk. So what we're going to do today is just talk about four of these. Okay, and I'll leave it up to you to go look at the documentation for form kit or if you find another form framework out there, go look at them. Here's the four things I want to talk about. First of all, the choice of having a single component for form kit. What that is, what does it mean and why did we do it? Second of all, input values are collected automatically. Third, apply validation with a single prop, and fourth, form generation. All right, let's start with a single component. What do we mean by that? Well, fundamentally, a form kit component is roughly equivalent to the input tag in HTML. So if you take a look here, we got form kit type check box and that is roughly equivalent to input type checkbox. Makes sense. Now, if you want to have a select in HTML, this is a different tag. But in form kit, it's still the form kit component. And this is applicable to literally every single input that form kit ships with.
7. Form Kit: Native HTML Inputs with Superpowers
Form Kit provides a core package that includes all native HTML inputs with added superpowers. It also offers paid pro inputs like auto complete, rating inputs, and repeaters. The generated markup is similar to native HTML, with added wrapper classes and automatic linking of labels and inputs. Swapping the type of input automatically renders different output. Form Kit includes a decorator for styling checkboxes. Multiple checkboxes can be easily implemented using the 'options' prop. The single component approach has a simple learning curve and a consistent API.
It kind of smooths out the API of HTML. So every single native HTML input is part of the core form kit open source package. We're talking about buttons and the native color wheels, the native date picker, text areas, text, all of those things that you're used to using in HTML are part of it, just with some Super powers that form kit adds.
And now recently we've started in order to try to support the project, the open source project. We've also got some of these like paid pro inputs that don't come native. So we're thinking like things like auto complete and rating inputs and repeaters and so on and so forth.
So what does it look like here is a form kit text input, we just say type text. And now we're getting to do some things like we were doing in our earlier example in native HTML and native view. We've got a label here, favorite color help, some help texts with it. And what does it generate? It generates markup that looks remarkably similar to what we had before. It's got some appropriate wrapper classes around it has a label as you can see here it automatically generated an I.D. for my input and then semantically linked it to my label. And it did the same thing for the aria described by like we were talking about before. And it applied that to the help text. Now you can just swap out the type and you automatically get different rendered output from the same component. So for example in this case I swapped it out for checkbox and now I'm getting a checkbox that's actually rendering in that location. And here you can see there's a form kit decorator because if you've ever had to do checkboxes you know that styling them is a nightmare because you can't really style the actual checkbox itself. So we are developers at form kit and we wanted to make that easier. So by default it ships with this decorator that allows you to make nice looking checkboxes like this.
Great. Now what if I want to do multiple checkboxes. Well in that case you can just say options and pass in the options that you want and the rendered output that you're going to get is dramatically different because in order to do multi checkboxes it's actually quite complicated in native HTML. Technically you should have something wrapping them like a field set so that we accessibility knows that these inputs are all part of the same group. And so here is the rendered output for multiple options and it looks something like this at least with our default built in theme and it works great. Now what are the advantages of the single component. First of all the learning curve is dead simple. You basically already know how to use form kit if you've ever written an HTML form before because we're copying all of those concepts over. There's a consistent API. Everybody on your team who's coming into a form can look at it and generally understand what's happening there because it works the same way across input. So most of the props are the same.
8. Automatic Data Collection in Form Kit
The components look the same. The way it passes data back and forth is the same. Input values are collected automatically in form kit. This allows for quick and easy structuring of data. It reduces boilerplate and minimizes errors.
The components looks the same. The way it passes data back and forth is the same. And it kind of smooths out those inconsistencies in HTML you know things like an input having the value applied via value versus a text area having the value be applied as the child of the input in form kit. This is just form kit type text area form kit type text.
OK the next thing to talk about here is input values are collected automatically. So here's a traditional view form. I've got a submit handler. I'll be modeling the address address line to the city the state and the zip code. None of this is actually necessary and form kit because it does it all on its own.
So I'm going to attempt a demo here pull up the playground and here you can see this is an equivalent form. In form kit I've just got a form kit type form and then I've got all of my inputs down below here. And to show you what I'm talking about I'm just going to destructure out of the default slot the value of the form you can do this with any form kit input. I'm going to do it with the form and I'll put a pre tag right here and render the value. And as you can see all of the values of the form are automatically being collected and they just will get handed to your submit handler automatically. That's pretty nifty but where this really gets exciting is if you want to create any sort of structure.
So in my case I'm going to create something like address dot view because I want to reuse this you know these pieces of the address. I'm going to take these I'm going to copy them and I'm just going to paste them over here. I'm not going to not in the set up in the template here. I'm not going to use anything else other than that. And in this component I'll just do a script set up and I will import address from address dot view and then we'll just render this. And what's great is my values are automatically still flowing up and I can do this nesting at any depth. And if I want to I can even group them.
So for example I could say form kit type group and I could call this name address. And I'm just going to wrap my address component in it or I could put this group in the address component. Now you can see that I've automatically in my form gotten the name address and all of the values of my address are going to flow directly underneath that namespace. This is a great way to just really quickly structure your data. So some advantages of having this be collected automatically. Well for one it's less boiler plate and that doesn't just mean less work. That also means less opportunity for errors. Form kit's automatically handling this for you.
9. Simplifying Form Building with Form Kit
Form Kit simplifies form building by eliminating the need for two-way binding and avoiding the complexities of dealing with nested components and large forms. It ensures data is where it should be, promoting reusability and making debugging easier.
So you can just build your form and then know that the data is going to be where it's supposed to be when it's supposed to be there. It encourages reusability like you saw. If I wanted to take a standard V model and apply it to an address component like that that's fine. It's not too hard. But as soon as I start to nest that down under repeated components it becomes a real pain because I have to pass those values like the model value and the emits through all of those components and pretty soon I'm gonna have multiple channels running up and down through there and it gets pretty hard to debug. It also avoids two way binding. You'll notice I didn't need to use V model anywhere. Now you can to be clear. You can use V model with form kit but I didn't need to. And if you've ever had to deal with a really large form where you're V modeling things more complex than a single scalar value, you know that it can get really hard to do that. There can be problems with infinite loops and recursion and things like that that happen because objects are fundamentally new even when they have their same values.
10. Applying Validation and Form Generation
Apply validation with a single prop and easily add validation rules to any input in FormKit. The validation at the top of the form automatically knows about the validation states of all the sub children. The learning curve is easy, the API is consistent, and validation rules are co-located on the inputs. The last feature is form generation, which uses the FormKit schema, a JSON serializable data format for storing DOM structure and component implementations.
OK. Apply validation with a single prop. So this is pretty self-explanatory but you know here I've got some checkbox of some ice cream flavors and it would render looking something like this and if I wanted to add validation to that all I need to do is add validation equals and then type the validation rules. Formkit ships with dozens of validation rules out of the box and all of them have internationalization support for the messages they produce as well.
So let's take a real quick look here. And sure enough here is my input and I can check these boxes. Nothing really happens. That's great but then I want to add validation. Say validation is required. And now when I check a box and uncheck it I get flavors required. OK and if I want to add another rule I'd say the minimum is two and I'm going to add three rules I must say the maximum is three. And now when I start interacting with my form you can see OK if I only have one it says cannot have fewer than two flavors. I check 2 all is good check 3 all is good go to fourth and you cannot have more than three flavors. And I can add that those types of validation rules to any input in form kit. They all supported out of the box right away even if you create your own inputs they automatically get all the validation support. And I should mention that the validation at the top of the form automatically knows about the validation states of all of the sub children so it's able to stop your submit your form submission until your validation is complete. If that's the behavior we like.
Some advantages. Again the learning curves easy. I mean it's so simple. I'm sure you basically learned all you need to know about validation by seeing that the API's consistent. Your team doesn't need to go look through validation models and other complexities. They can just look at the input and see what it is. And that's the third benefit is that co-located your validation rules are on the inputs that they're being used on.
Okay and then the last feature that I want to talk about which is a whole subject in and of itself but it's form generation. So in order to generate forms and form kit we have something called the form kit schema. Let's take a look. Formkit schema is a JSON serializable data format for storing DOM structure and component implementations including for formkit forms. It's kind of wordy but let's let's see what we mean by that. Here is a simple HTML div just a div with hello world.
11. FormKit Schema and Features
FormKit schema allows you to represent forms in a serializable format, which can be stored, generated, and used to automatically create markup and forms. It supports rendering DOM elements, components, and shorthand for quickly rendering FormKit inputs. Expressions can be used for Boolean logic, arithmetic, function calls, conditional rendering, and slot rendering. The schema can be passed as a string and interpreted into render functions. Reactive values can be rendered, and formatting functions like INTL currency can be applied. FormKit schema simplifies form building and offers many features to make the process easier.
Now we can represent this in schema using something like Now this is not JSON but you can see obviously this is easily serializable to JSON just $EL div and then children hello world. If we were to pass that object to formkit schema it would actually render the same div. But why is that important? Well that's important because we can take that schema and we can put it anywhere we can put it in the cloud we can store it in a database we can send it over the wire we can construct it we can do all kinds of things with it and then use that to automatically generate markup and forms.
So in in the formkit schema there are three special elements that can get rendered one is a DOM element. You just do that with dollar sign EL. Second is dollar sign CMP that renders a component and you can pass props to it just like you would pass props to a component interview template and then third is a shorthand and this shorthand just allows you to more quickly render formkit inputs. You could use the dollar sign CMP as well if you wanted to since formkit is just a component. But what really makes it powerful is expressions formkit schema supports actual expressions. What do we mean? Well you can write Boolean logic basically in your Json equals ands ors does not equal things like that. You can support entire arithmetic so we're talking addition multiplication subtraction exponential math all of that can be written directly into your Json. It supports function calls conditional rendering you know like render this or or this based on some condition slot rendering and let's take a little look at some of this so here we have a formkit schema component and you can see that we are passing schema directly into it which is just this little object right here. Formkit form has a submit label and then as a child inside of the form we have a number.
Now a valid piece of formkit schema is actually just a string so as long as I start my string with a dollar sign it tells this little compiler to boot up and interpret this into render functions. So if I just ask for for example the value of my form and the quantity this will automatically render the value of my input and you can see it's all reactive because it renders it to view render functions and that's not all I can actually write arithmetic here right. So I could say you know by some price you know the quantity times a price and here you go now we're actually getting reactive values rendered on the page. The other thing though that we're passing to the form kit schema is this data object and it's just a view reactive object and it has only one thing in it it's got those dollars. And if you look dollars is just the INTL currency function. So I'm getting USD format and I can actually call that function right here. Dollars I'm going to just pass these values directly into it and sure enough now we're getting formatted numbers. And if I want I can pass other values too. So for example let's say the price is you know 233. I could just reference the price now now that I'm passing it in dynamically. So just using dollar sign price and that matches what's in my dynamic object and now I've got this rendering automatically. That's a really really brief look at form kit schema. I would love to spend a lot more time talking about it but we'll save it for another day. So again those are only four of the features that I wanted to cover. There's a lot more there and it really does help build your forms and make it much more easy which brings up the question are forms easy? It's a really valid question and I would say they're really only easy if you're using something like a form framework. That's it for me. Thank you so much. Really appreciate getting to talk here. We've got the documentation at FormKit.com. You can follow me on Twitter at JP Schroeder and the Twitter handle for FormKit is useformkit. And just one little plug here View School actually did a whole series of courses on FormKit which you can go get at viewschool.io.
Comments