5.3 Template-Driven Forms: Validation and Submission
In this lesson we'll see how to add form validation using nothing but regular HTML5 attributes. I'll also show you how to work with form instances and data submission.
1.Introduction6 lessons, 42:00
2.Get Started With Angular7 lessons, 42:38
3.Core Concepts7 lessons, 55:20
4.Template Deep Dive11 lessons, 1:10:56
5.Forms10 lessons, 1:45:41
6.Routing9 lessons, 1:15:10
7.Using the HTTP Client5 lessons, 56:24
8.Testing10 lessons, 1:23:27
9.Building for Production1 lesson, 03:40
10.Conclusion1 lesson, 01:32
5.3 Template-Driven Forms: Validation and Submission
Hi folks, In this lesson we're going to continue looking at template driven forms this time covering validation and form submission. In the last lesson, we wired up the player name input and bound it to a property of the same name in the component. So that was the planning property, the problem is though we can still start the game with an empty [INAUDIBLE] That's not good because as you can see in the console, the player doesn't have a name. Let's just put the player name property to how it was, as well, not every player is gonna be called Dan. So to fix our current problem, where we can start again with the input being empty, we need to add some validation. With a template driven form, we do that in the template. And we can do that just by adding one of HTML 5's built in form validation attributes, so let's specify that this input is required. So we've added the required attribute to the input, this is a regular HTML5 attribute. But if you recall from the last lesson, Angular already adds the no validate attribute to the form automatically. So the browser's built in validation won't kick in. Let's go back to the browser briefly. And let's inspect the element. And we can see straight away that the input now has the class ng invalid, and so does the form itself. So now let's type something into the field And we can see now that the input has the NG valid class, and so does the form. Perfect, so earlier we were using the NG class binding to manage warning and error states for the input. But this was just an example of how to use NG class. We know that Angular is managing some form classes for us. So we could make use of these. But the class names are added only to the form element and the individual controls within the form, but we want the styling to apply to the label as well as the input. So let's stick with the NG call binding, but update it. So we're not gonna be linking to this state object anymore. Instead, we want to add a class called, error, and we want to do that when the form is not valid. So we're saying here that we want the error class to be added only if the form's valid property is false. Remember, in the last lesson, we added the ngform directive to the form elements, and we associated it with a template reference variable called playerForm. So we can use this to reference angular internal model of the form. This will have a valid property when the form is considered valid. Let's go back to the browser again. So the problem that we have at this stage is that as soon as the form loads, Angular sees that the input is empty and treats it as invalid. This is the correct thing to do. Technically the form is invalid because the input is empty. But the user hasn't even started interacting with the form yet, so it's a little unfair to start screaming error at this stage. So let's fix that by only showing the error styling if the form has been touched and the input is empty. So, we want to check that the form is not valid, before we add the error clause, but also that the form has been touched. So, now we can see that the error styling has not been applied but if we focus the input And then blur it. The error class does get added, so let's add a little error message as well which instructs the user what's actually wrong. We can just add that after the label and the input here And we can just provide an error message. And we want to show that on the same condition that we add the error styling. If the form is invalid, that the form has been touched. We will also want to add some new styles for this error message. We can do that in forms.scss. We just add it down at the bottom here. So while we're in this file we can rid of the warning styles, we won't be needing these at all. So let's go back to the browser and just check that everything is still working. And we can see that when the page loads, the form doesn't have any error signing applied. So let's just focus the inputs and then blur it again and now we see that we get the error styling and we get the error message. The problem is that we can still start again when the form is invalid. The reason why this happens is because we have the click event binding on the start button. So we have this startGame event here, and even though the button is inside the form, we haven't given it an NG model directive or any other form directives at all. So angular doesn't know anything about this button. And that's why we can still click it and we can still start again even when the form is invalid. So, let's fix that. We can remove the click handler as well as the disabled attribute from the button. So, once we've got this wired up the button doesn't need to be disabled because the player won't be able to start again until they've entered their name. [INAUDIBLE]. So there's no reason for the button to ever be disabled. What we want to do is use the submit event of the form instead. So we're still calling the same method that we did before, its just connected to the submit event of the form instead of the click event of the button. We learned earlier that passing the event object arround is a bit rubbish, so we've removed this also. So, let's go back to the start component now. We'll need to make some changes to the [INAUDIBLE] method. So, we aren't parsing the event object anymore. So, let's just get rid of that. But, we will need a reference to the form inside the component. We already have this reference in our template, so we can pass this through to the method from the form. So the reference that we have to the form is this template reference variable, player form. So we can use that to pass the whole form through to the component. And as we don't have the event object anymore, we won't be able to call prevent default on it, so let's get rid of that. So we don't call in prevent default anymore let's just go back to the browser briefly. So the page isn't being reloaded anymore, that's good. But there is still some work to do at this stage we can still start again by pressing the button. So what we want to do now is update this method so that it only emits the start event if the form is valid. Now, I already know what we have to do to make this work. But I won't always know what needs to be done, and I'm sure there are some of you Watching this, they don't know what to do at this point either. So let's make things easier for ourselves. Let's add some type information and we should then get some IntelliSense in the editor. So first of all, let's bring in a class called phoneGroup. So we get the from group class from the forms angular module So back in the star game method, this is going to be receiving the player form, and the form will be of the type form group. So first of all then we want to wrap the emit method in an 'if' statement. So because we now have type information for this playerForm, as soon as we hit that first dot after we type playerForm Then, we get this lovely IntelliSense box, and that tells us all of the different properties and methods that we can use on a form group. This is really, really useful, and quite often, if you are coding something for the first time, that you haven't worked with before, this kind of IntelliSense can be a lifesaver, because you don't even need to go look up the documentation online. You can just use IntelliSense. And they give to you this little tool tip, that even tells you what the property or the method does and what it's used for. So, this is another way in which the type of information is really, really useful. So, at this point, we just want the valid property of the phone, so we can say, if the phone is valid, then go ahead and commit the start events. We also want to add an else branch to this if statement. So, let's just go back to the browser briefly, before we add any more code. So, at this point, we should find that we can't start the game now if the input is empty because it simply won't Emit that start event, but there's no real indication, after clicking the button, of why the game can't be started. Obviously, we know that it's because the input is empty, but a user visiting this app for the first time probably won't know that. So that's what the else branch is for, what we really need to do is to say to angular, look If somebody clicks this button, and the form is not valid, you need to show the error message and the error styling. That's what we're going to do in this else branch. So we'll start out with the playerForm, we definitely want to do something to that form And we hit the dots and we get the IntelliSense. So we know that a form in Angular has a collection of controls and we know that this object of controls should contain a control called name because our input has a name attribute with the value name. And what we want to do is mark that control as touched. So there's a method we can use called mark as touched. There is a few other very similar methods. We can mark something as pristine or untouched but in this case we want to mark it as touched. So let's go back to the browser again. And it would've already reloaded by the time we come back here so if we click the bottom while the input is empty an angular will show the error message and apply the error styling. So now the game can't be started until we actually add a player name. So there's a couple of properties in the class that we can safely get rid of now. So we were using the disable button property to control whether the button was disabled. We don't need to disable the button at all now, so let's get rid of that. And we were also using the states objects earlier on, so let's get rid of that as well. And we can pretty much get rid of all of the code inside the ngOnInIt. Awesome. So, in this lesson we saw how to work with ng-valid and ng-touched classes to show error information when the form is in an invalid state. We saw that we can add regular HTML5 validation attributes, such as required, to tell Angular how to handle the validity of the form. As well as required, we can use any of the built-in browser validation Validation attributes, and angular will apply the same validation logic that the browser itself would apply. Which is pretty useful, we also saw how to work with form submission. And we saw that we can use the submit event of the form to add a binding to a method in our class. We saw that we can pass a reference to angular internal model of the form into our own class and work with this inside the component, We also saw that we can add the phone group type which gives us IntelliSense and the editor. And this is super useful when we're working with forms. Because there is so much that you can do with them. The example app would still function in the same way without this type of information. It just makes our lives much easier. Thanks for watching.