![Vanilla JS: You Might Not Need a Framework](https://static.frontendmasters.com/assets/courses/2023-07-06-vanilla-js-apps/thumb.webp)
Check out a free preview of the full Vanilla JS: You Might Not Need a Framework course
The "Binding Form Data" Lesson is part of the full, Vanilla JS: You Might Not Need a Framework course featured in this preview video. Here's what you'd learn in this lesson:
Maximiliano demonstrates binding the order form's input data to a user object. Clearing out the form when the order is placed and updating the home page to render when navigated to are also covered in this segment.
Transcript from the "Binding Form Data" Lesson
[00:00:00]
>> Okay, so I still have one more thing for you that had to do with reactivity also with proxies and because somethings that sometimes we when we are doing vanilla JS we miss from frameworks. The ability to do binding and double bindings, okay? For example, if I have here a form, when you place an order, the code to place an order, typically needs to go element by element reading the value, right?
[00:00:35]
So like document get element by ID name.value document get element by ID phone.body, right? it's kind of, right? And when you have Angular or React or Vue you can create like a data model and assign the data model somewhere somehow or have a double data binding. And can we do something like that in vanilla JS like when you're typing something and you change the input that, can we automatically update an object?
[00:01:12]
And vice versa, if we change the object, can we have an automatic change in the input? Well, yes, we can, and for that we can use the same idea of a proxy or we can mix the proxy with other techniques, and I'm going to do that with this form.
[00:01:32]
That's the last chapter that I have here, that is how we can do binding for forms. So for this, I'm going to create like a data model object in our OrderPage.js and I'm not sure if you will like it because I know it takes time to like this.
[00:01:53]
I'm talking about the JavaScript syntax to define private properties. So on OrderPage.js, it's a class, I'm going to do this, have you used that before? Before that hash before a property, that means it's a private property, so in TypeScript, this is going to be like private user, okay?
[00:02:17]
But this is not TypeScript, this is JavaScript, plain JavaScript, it's just this. So then it's a private object data model where we can actually have, the source of truth if you want to avoid duplicating the data in different places, so we create an object. And then we wanna try to bind automatically that object with the form in both ways, so if I change the object, I want the form to receive the change and if I change the form I want the object to get the data.
[00:02:51]
So we are going to create a new function here a new method on the OrderPage let me do that at the bottom setFormBindings, okay? That receives a form and you will try to do bindings, okay? Over that form, so we're going to set what is known as a double binding, okay?
[00:03:14]
Double data binding. So, how to do that, for example, let's try to think about the proxy, so for example, I can take this.#user and this is how it looks like this.#user. So I will take that #user and I will say, you know what? I'm going to create a new Proxy from you, and even here, replacing the original property with a wrapper, okay?
[00:03:43]
Of yourself, and then we have the handler, remember, and the handler has traps, so I'm going to trap set. Set receives a target a property and a value and we're going to say here for first because it's a set and we don't wanna validate it's always the same thing here.
[00:04:03]
We actually make the assignment, okay? Yeah, you wanna set the value? Yeah, let's set the value first, and after that, I can, for example, say something like this. Let's take the form, the form is something that we receive as an argument, and the domain API has a way to access us as a collection.
[00:04:24]
All the elements of the form, although not every HTML element, only the elements that are for the form inputs, text areas selects, so in this case, I can say form elements form.elements, okay? And elements, it's not just an array, it's also an object, so, let me show you this in action, but also this will lead to some special issues that we may have, so what if we want to get the form in the console?
[00:05:11]
How to do that, can we do document querySelector and give me the form? It's null, why? Maybe you say, maybe you forgot the form tag, well, let's say input we can see the input on the screen, why? It's no, it's anyone know?
>> It's a shadow document.
>> It's a shadow DOM, so it's in the shadow DOM and here in the console we are at the top, can you see that?
[00:05:42]
So that's a problem that we have, so, we need to dig into, from the window, need to dig into the form to try to get that, okay? If you inspect this, and you get into the form here, okay? So the form is there, okay? So you say "hey, we can store it as a global variable", and use these tricks here, and then, yeah, we have temp 1 now, okay?
[00:06:14]
Then temp 1 is a form, that's one-way to get rid of that DOM, and I can show you from here that I have elements. And elements, it's a form control collection, and elements can also work with .syntax, so .email, .name .phone, that's it. That's a very old DOM API by the way, that will let you jump through all these elements and they're using their name, the name property as the name of the property in JavaScript, okay?
[00:06:51]
Makes sense? So I can get the phone like this, and that's the input, and if I want the value, it's value, so if I have a phone number here, I can say the form.element.phone.value. And I'm not sure if you know this, but in JavaScript, instead of using the .syntax, you can also use the square bracket syntax but with strings, did you know that?
[00:07:17]
So based on that idea we can make this auto binding solution, now going back to our code, and use form.elements subproperty.value=value. So we are not just setting the value of my user, private property, but also the value of the form, so every time you change the.#user, we are also changing the form, okay?
[00:07:45]
Does it make sense? So that's one-way, we want two-way binding, that's one-way, how we make the other way, well, in this case, we are going to talk to the form, and we're going to talk to the elements of the form. And for each one, we're going to listen for the change event, does it make sense?
[00:08:09]
The problem is that this is an array collection of which type? It was temp1.elements, an HTML form controls collection it doesn't contain for example forEach. So we can do a normal for expression or we can wrap this in an array and then we can do for each up to you, okay?
[00:08:39]
And then we say forEach element, forEach form element we are going to add an event listener for the change event. Okay? And here, we're going to take our user and change the property for element name, the value, so this is the two-way binding if you change the form, we are changing the.#user.
[00:09:08]
If you change the.#user, you're changing the form. Some of you may be thinking, but we have an infinite loop problem here, because if we change the.#user, we are changing the form and then the form changing the.#user and then the form. But that's not true, because when you're changing form.elements from JavaScript, so in this case, this code line 73, that's not triggering a change event automatically.
[00:09:38]
The change event in the DOM, is triggered automatically and by default only if the user is changing that in the UI, so when you are changing form.element from JavaScript, you won't have that problem. Does it make sense? I'm not sure if it makes sense, but I didn't write them that implementation W3C, so that's why we don't have that infinite loop issue here, that kind of handshake.
[00:10:07]
Okay, so why are we doing this? Well, first, we should call setFormBindings somewhere, and we can call this here on render, when we are rendering, or when we are constructing the form, it doesn't matter. So we are going to call setFormBindings, and we need to pass the form.
[00:10:30]
Please remember, don't use querySelector form we know it's not going to work because we are in a shadow DOM so document is not what you want we want this.root is the shadow DOM. Okay, makes sense? That's the Shadow DOM. Does it make sense? So how do we know if this is working?
[00:11:01]
Every time we work with shadow DOM's knowing if something is working or not, takes more time, because it's not easy to inspect that okay, so how do we test this? First, we have a problem, the problem that we have is that we are calling setFormBindings at the beginning of the render method.
[00:11:20]
And at that point we didn't load the template yet, so we don't have the form, so the form is actually null because it doesn't exist. So I need to move this at the end of render. So to use this, because we are using a private field, the.#user it's difficult to actually debug or see that within the console.
[00:11:42]
Because it's actually encapsulated within a web component that is a shadow DOM component, so it's actually really complicated to get into that value. So let's go and use it directly within setFormBindings, we can also listen for an event for when you click send the order, okay? So when you are here and you have a form, we know that we have a place order button, so maybe you're thinking, well, let's listen for the click of that button.
[00:12:17]
But that's not how we want to do this in HTML and vanilla JavaScript, why? For several reasons first if you're on a mobile device typically you have a beautiful keyboard on the beautiful keyboard, you have a go button or send. And if you click that the user expects the form to be submitted the same if you press the Return or Enter key in your keyboard on desktop.
[00:12:45]
So instead of listening for the click event of the button, we may want to listen for the submit event of the form. There is going to be trigger, if the submit button is clicked, or if you wanna submit the form from a different way such as the virtual keyboard on the iPhone or on the Android.
[00:13:06]
So we're going to do something where we submit, first, what we want is to stop the browser to submit the form using GET or POST to the server, that's the standard form behavior. And for that, we have already used this before, we will call prevent default event.preventDefault, so we won't want the the browser to submit the form to the server and change our navigation, okay?
[00:13:36]
Then we can make an alert or in the future you can make a nicer UI using alert for designs it's not a good idea, yeah? But yeah, I'm the trainer here and I have the power to use alert, but have in mind that's not a good idea, so I'm going to say for example, Thanks for your order, and I wanna use the user's name.
[00:13:58]
Should I go to the DOM and listen for the input value? Now we did a double binding to our #user, so if this is working, I should be able to just say this.#user.name, okay? Something like that. And of course I can also say that, you will receive your receipt at your email.
[00:14:24]
So after doing that. We should clear the form, right? So we delete the.#user, the name, so it's not, we clear the form. So, what if we just clear the user? Like the properties one by one. If the double binding works, it should clear the form, does it make sense?
[00:14:56]
So this is a quick way to check if the double binding is working or not, and here we have left to send the data to the server. Okay, make sense? So let's try this. I'm going to add some items to the cart, I'm getting here, I'm going to type Max my phone number, my email, I'm going to place the order.
[00:15:29]
Thanks for your order Max, so that work, OK, the form is clear but just one part. And let's see what we have here, the problem that we have in the proxy, if you look here, it says trap returned falsish for the property phone or email, so what's the problem here?
[00:15:59]
It has to do with the proxy, remember I mentioned this every time you call set, you need to return true or false. And if you don't return anything, it's falsish by default, not false falsish, which means yeah, you didn't tell us what happened? So that was the problem, so try one more time.
[00:16:28]
So Max, phone number, email, place order OK, and now everything is cleared out, so this is the proof that our double binding is working. Again, you may see that we have, I don't know, 15 lines for the double binding, and you may be thinking, well, that's easiest on whatever framework, I'm not sure that's true.
[00:16:57]
But anyway you can add abstraction layers on top of that you can create a function you can create a mini-library to make this even easier to make that double binding between HTML and data objects. Using proxy for data to HTML and using events, or even there are other events like observers, APIs that we have available today to check changes in the HTML and update the data model.
[00:17:30]
So we still have one bug in our project, so when we're getting into details or event cart, and we're going back to the home, we don't see data, so why is that? If we go to MenuPage.js, we're calling render only when the menu is changed, So what's the problem?
[00:17:53]
We loaded the menu once when the page loaded for the first time when the on DOM content loaded, so yeah, it worked the first time. But then the problem that we have is that when we are disconnecting that page and connecting another instance of that page, the data is already there, so it's not changing.
[00:18:13]
So what I need is to just call render the first time Without I mean on connectedCallback, without waiting for another change. So every time you are connecting this web component, I wanna render the data, not matter is the data is changing or not, okay? What happens the first time of this render?
[00:18:34]
The menu will be null, so it will render loading properly, it's going to be really faster we won't see it unless I stopped the server or something like that. But now, I can go to the details, I can go back to home and it works and go here, now we have full navigation between the three pages, by the way, I can also add items from the details.
[00:19:02]
And here we are also going to the cart, I can delete elements and our app is ready, our vanilla app is ready, and it should be really fast. Because you've seen all the JavaScript codes that we have written, it's more verbose than some frameworks, it depends on the framework, actually, to be honest.
[00:19:26]
But in terms of the code that you are shipping, no in the code that you're writing, yes, probably, and that's why I was mentioning that you can add more abstraction layers. On top of this, to make things easier for you when you understand what's going on.
Learn Straight from the Experts Who Shape the Modern Web
- In-depth Courses
- Industry Leading Experts
- Learning Paths
- Live Interactive Workshops