What do you want to learn?
Leverged
jhuang@tampa.cgsinc.com
Skip to main content
Pluralsight uses cookies.Learn more about your privacy
Angular Forms
by Mark Zamoyta
This course will cover the fundamentals of working with forms in Angular 2. You'll learn how to create forms, style them, and use data binding and validation. You'll be able to post a form to a server, and work with third-party form controls.
Resume CourseBookmarkAdd to Channel
Table of contents
Description
Transcript
Exercise files
Discussion
Recommended
Course Overview
Course Overview
Hello, everyone. My name is Mark Zamoyta, and welcome to my course, Angular 2 Forms. I am a software consultant and developer in the Portland, Oregon area. The information you gather from your web application's users is vital to your company's success. You want to make data collection simple, and provide a great experience for your users. This course on Angular 2 Forms will show you how. Some of the major topics that we will cover include building forms with Bootstrap styling, data binding forms to your application's model, validating form input and handling errors, and posting a form to a server, handling any success or error codes. We'll also look at several third party form controls to help build better forms. By the end of this course, you'll be able to build great-looking forms that offer your users a simple way to input any data. You'll be able to validate and post form data to a server for storage. Before beginning this course, you should be familiar with fundamentals of Angular 2. Two great Pluralsight courses for this are Angular 2: Getting Started and Angular 2: First Look. I hope you'll join me on this journey to learn all about forms with the Angular 2 Forms course at Pluralsight.
Introduction
Course Introduction
Welcome to Angular 2 Forms. My name is Mark Zamoyta. Forms are all about collecting data from your websites or web apps users. You want to make the process of filling out a form as simple as possible while also making your form look good. That's what we'll cover in this course. Here's the course layout. This introductory module is mainly an overview of the course but we'll also go over getting set up with a startup project. In addition we'll discuss the difference between Angular 2's form technologies, template driven forms and reactive forms. This beginner course is mainly based on template driven forms but a lot of that also applies to reactive forms. In module 2 we'll get our first form set up and use Bootstrap to style it. We'll see how Angular works with the traditional form controls. In the course demos we'll be putting together an employee form. Module 3 is about data binding. Angular 2 helps us move data from our forms' component to the form in the browser and back again. This is two-way data binding and we'll see how to bind various form controls to a java script or a type script object. Module 4 we'll cover form validation. We can use html 5 attributes for validation and Angular will work with them. We can also call functions on our form's component. We'll see how to style error messages and give our users a smooth experience when filling out a form. In module 5 we'll post a form to a server. We'll create a simple node server for testing and we'll post it using Angular's http module. We'll see how observables work, handle any errors along the way, and gather up any data after a form post such as a newly generated employee ID on the server. We'll wrap up the course by looking at some open source form controls in the NG2 Bootstrap library. We'll work with a date picker, time picker, toggle buttons, horizontal radio buttons styled with Bootstrap and a rating control. By the end of this course you'll know all of the key features of Angular 2's template driven forms. You'll be able to put together a form with various types of fields, data bind to a component, validate the form as a user fills it out and post it to a server handling any success or error codes. So let's get started. In the next clip we'll get set up quickly with an Angular 2 seed project.
Setting up a Demo Project Using angular2-seed
Setting up an Angular 2 application from scratch is a big feat. There are many files involved and it can get complex at times, so in this course we're going to be using the Angular 2 seed project. This project has everything we need to start experimenting with forms. I'm here at GitHub and we're looking at the Angular user and the project name is angular2-seed. And if you search for angular2-seed around GitHub you're going to find a lot of different packages with that name so make sure you're at the Angular site. And one thing you might want to do when you get started is to scroll down and look at the package.json file. We just want to see what version of Angular they're using. And let me zoom in a bit. We can see that this package uses version 2.0.0 of Angular. So that's fine. I'll scroll back up and let's clone this repository. I'll go back to the home page and there's a clone or download button. And I'll just hit this copy button right here to copy it to the clipboard. So let's clone this at the command line. One thing I should say is that when using Windows you need to run your command prompt as administrator. Just right click on the icon for command prompt and click run as administrator. Several permissions problems could pop up if you don't do that. I'm starting off in a folder called ps for Pluralsight and let's enter in our git clone command. I'll hit control v to copy in the project name at GitHub. So if we run this now it'll create a folder called angular2-seed, let's do that. And we've cloned the project. If you wanted to rename it I'll recall that last command, we could just enter a new folder here. I'll just call it foo-dir and you can see that it cloned into a directory called foo-dir. So you might want to do that to rename the package immediately as its cloned. But I'll just remove that for now and I'll work in angular2-seed. I'll go there, I'll cd to angular2-seed, and we're at the root of the project. You can use any editor to work with these files for this course but I'm going to be using Visual Studio code. And Once I'm in a folder I can start up code easily by typing code and dot for the current directory. So that started up code. Let's take a look at package.json. I'll scroll down just to make sure we have that same version of Angular and we're at 2.0.0, that's fine. Most likely when you run this the version will be incremented. Now the next important step we need to care of is to actually load in these dependencies. Let's go back to the command line. We can load in the dependencies by calling nmp install. This will look in package.json and install everything we need. So everything looks like it installed and we have two warnings that we see a lot that aren't a problem. So now that we have the dependencies installed we can go ahead and run the server. We can run it with the command npm start. That might take a while for webpack to get up and running but we got our webpack bundle is now valid message and we're good to go. So during this course the way I'll work mostly is with Visual Studio code open on the left side of the screen and I'll have the browser Chrome open on the right side. And what I'll do is I'll navigate to the url that the server's at. It's at localhost:3000. And here's our angular2-seed project up and running. So there's a little menu here, there's a home component which we're looking at, an about component and a GitHub repos component. And we're not concerned with about or the GitHub repo links. And we're also not concerned with routing at all. So we'll be working mainly with the home component in this course and this is where we'll put our form. I just wanted to get set up for now and we are all set up and we'll go into more detail in the next module.
Template-driven Form and Reactive Form Technologies
Let's take a look at Angular 2 form technologies. There are two different technologies we have a choice of using in Angular 2. One is template driven forms or we can use reactive forms. This course is about template driven forms but we'll take a look at the main features of both of these. So for template driven forms they use a components template and you're probably familiar with this if you've seen one of the fundamental courses on Angular 2. Here's an example of a component and you can see the template url which points to the template file. And here we have the template file which is our html markup. And this is where we would create our form in template driven forms. Because we're mainly using html to create the form, if you're writing unit tests you're going to need to test against the DOM and this could be difficult and slow in a lot of circumstances. For example, your validation might exist in html 5 attributes such as required or middle length or max length. And it's usually up to the browser or the DOM to take care of that. So the alternative to template driven forms is reactive forms, let's take a look at that. If you decide to go with reactive forms you're also going to need to create a template for your component just like you did with template driven forms. But the big difference is you're also going to have to create a for model in type script. The for model is created with an Angular 2 API which lays out your form in type script in code and this for model must be in sync with your component's template. So you are repeating yourself somewhat and you need to make sure your template and your for model always stay in sync. But the big benefit of this for model is that you can unit test against it, and that could make things a lot easier and a lot faster for testing. Another feature of reactive forms is that the validation doesn't necessarily need to be in the DOM. You can put the validation directly in the for model. So that said we're going to be focusing on template driven forms in this course and a lot of what we learn also applies to reactive forms when you're ready to take that up.
Note About Earlier Angular 2 Versions
Hopefully you'll be able to work with the latest version of Angular 2, but there are many projects out there which were started in the beta or release candidate stage and I just want to talk about those for a second because forms made a drastic change on Release Candidate 5. If you have a project that already exists and its Release Candidate 4 and earlier, it's using an old forms API. And if you look at how the Angular 2 applications starts up it bootstraps a component and Angular doesn't work that way from Release Candidate 5 on. Currently we're at a 2.0 release in Angular but probably by the time you see this it'll be 2.1 or higher. But the main thing I wanted to point out is if you're working with code that's Release Candidate 4 and earlier you've got to upgrade it. And I started this course when Release Candidate 5 was released and everything continues to work fine in the most current version of Angular. So that wraps up the introduction to this course. In the next module we'll take a look at setting up a project and creating our first form.
Form Basics in Angular 2
Introduction
Hi, my name is Mark Zamoyta, and welcome to this module titled Form Basics in Angular 2. We're going to jump right in and create a form with Angular, and I'm assuming you've got a simple, base project to work with. If not, check out the introduction to this course. Where I walk you through a few simple commands to get the Angular seed project up and running. We'll start off this module by reviewing the Angular seed project. And where we can create a form within this starting point. If you're not interested in angular-seed, and you're using your own custom project, you'll just need a simple Angular 2 component set up to contain a form. Next we'll build a simple form and learn about Angular's ngForm directive, and how it automatically gets placed on all forms. We'll access this directive using a template reference variable. Browser validation can get in the way, so we'll see how to shut it off by using the novalidate attribute on a form. We'll cover validation in much more detail later in the course. But, specifying novalidate on our forms now will help us avoid some browser inconsistencies. Next we'll load Bootstrap into our project for form and control styling. We'll start by styling text boxes. Then we'll take a look at adding checkboxes and radio buttons to our forms. These will be styled with Bootstrap as well. We'll look at adding a select control with options to our form. This will be styled with Bootstrap as well. We'll see how options can be hard-coded into our template, or they can be retrieved from our Angular 2 component. Later in this course we'll see how we can load the options over HTTP. Finally, we'll review browser inconsistencies when using certain input types, for example, an input tag can legally be set to date, according to the HTML5 specification. The problem with this is that different browsers have different ways of presenting this to the end user, leaving an inconsistent experience for users. You'll understand the need for third party controls to handle this problem, which will be covered later in this course. Here's a slide that we saw earlier, in the introduction of this course, but I want to take another look at it, because it's very important when it comes to setting up an Angular application for forms. When we look at all of the Angular Release Candidates, it's Release Candidate Four and earlier that use an old forms API. And you can also notice these types of projects, because they bootstrap a component. And the turning point really came in Release Candidate Five. All the way up to the 2.0 release. They use a new forms API, and they bootstrap an NgModule, rather than a component. And the good news is that Release Candidate Five, all the way up to the 2.0 release, the forms API hasn't changed. And you'll see this in this module. I recorded it earlier, when Release Candidate Five was out, but everything has been tested and still works in the recent 2.0 release. So let's take a look at our Angular seed project, get set up and create our first form.
Reviewing the angular-seed Project
I'm all set up to use Visual Studio code, as we saw in the introduction. I cloned the Angular 2 seed project here in ps. Angular2-seed. The ps is just for Pluralsight. And I already installed everything. You can see that node_modules is all set up. So let's run Visual Studio code by typing code and dot for the current directory. So here's code running, and I also opened up the Chrome browser to the right. This Angular 2 seed project does use live refresh. So we'll just keep these open side by side, going back to the command prompt, let's kick things off. I'll type npm start. And let's browse to localhost:3000. And our app is up and running fine. Let's also hit F12 to open up the console. And we can see there are no errors there, that's good. Now, the seed project has a lot going on inside it. Let's open up the source folder, and we'll take a look at ap.module.ts. Since this is a course on forms, the module we're interested in importing is this forms module. And the reason why we import it is that we can use it in our NgModule attribute, right here. And in the import section, you can see that it's already set up. We are importing the forms module. Now, there are a lot of other details in here that I won't go into, mainly because they're part of the Angular 2 seed project, and you can remove them if you want. For example, this whole GitHub import here has to do with this GitHub Repos navigation point. And of course, we're not going to deal with anything like that in this course. So the key thing to look out for though is make sure that we're importing the forms module, and later on in this course, we'll need the HTTP module as well. And that is also imported right here. Next, let's take a look at the Bootstrapping section. We'll go to main.browser.ts. And you can see that we're importing platformbrowserdynamic. All the work we do is going to be within a browser. And we're also importing our AppModule. Now, I'm working with Release Candidate Five. And this has a much different way of bootstrapping Angular 2 applications than prior Release Candidates. We called the Bootstrap Module method, that's directly on platformbrowserdynamic. And we pass it the AppModule. In earlier Release Candidates we would actually pass it our startup component. But, as we saw in the introduction, AppModules are a much better way to go. And we bootstrap with the AppModule now. So, the Angular 2 seed project sets all this up for us. And we're ready to start working with forms. If we look at our AppModule.ts file again, you can see that the component that we bootstrap is AppComponent. So, let's look at that. I'm looking at app.ts. And you can see that the template URL is in app.html. We'll look at that. So here's the markup that we're using to get this page within our browser, Chrome. And I'll hit the Home button and we really don't need all this information to be working with forms. So in the next video we'll clean all this out, and we'll actually create a form.
A Simple Form and ngForm
We're all set up, so let's create a form. We'll take a look at AppModule, and we can see that for our bootstrap property the module is going to boot up with AppComponent. So we'll look at app.html, and this is the markup that we see in our browser. And we want to cut this out. So all we have is a main tag and our router outlet. We'll just keep the routing in this application, even though we're not going to be using it. You can add your own routes later, or you can just cut it out on your own. But, the default route is going to be home. So, let's take a look at home.ts. And essentially all this is is a CSS file and our template, home.html. And this is where we'll be working in this course. We'll create our form here. We'll make a heading of Angular 2 Forms, and let's create a normal HTML5 form. So, I created a form tag, and it's not doing much. It just has a text field. Input type of text. And it has an OK button. So I'm just going to save everything, and here we have our form. It's got the title Angular 2 Forms. And the text field. Now, nothing's happening in this form. If we click OK nothing happens. We don't have any kind of posting set up. So it's pretty useless right now, but there is one very important thing going on. When we use a form tag in an Angular template, Angular 2 is going to add a directive automatically to this form. And the way you access a directive is with a template reference variable. So let's create a variable called #form, and we'll set that equal to ngform. So the directive that Angular adds to form is called ngform. And it exports itself with that same name. So this template reference variable, #form, is the actual instance of a directive. And we can also access that directly from this template. We'll bind to form.pristine. And pristine will just tell us whether or not the form has been touched or not by a user. If any data has been entered. We'll cover this more in the module on validation. But, for now, let's save this and see what we get. So we get true showing underneath the Name field. So the form is pristine, it hasn't been touched yet. But, you'll notice if we do enter characters into this field, Angular thinks the form still is pristine. And the reason for this is that we have to let Angular know which fields it should be interested in. Right now, it's not aware of our text field at all. So, let's go to our text field, and we'll give it a name. And we'll also let Angular know that it should be aware of its value. And we do that with the ngModel directive. I'll save it. Now when I start to type in a name, pristine should go from true to false, and it does. And even if I backspace, pristine stays as false. The form has been modified to some degree. So this ngModel directive, that we've placed on the input, this is part of the forms module. And that's why we have access to it. In Angular 1, ngModel is used for data binding. And it's used in Angular 2 for data binding as well. But even though we're not binding any data here, Angular is still becoming aware that we want it to control this field. So we can specify ngModel without specifying any data to bind to. And also when using ngModel, we want to make sure that the field has a name. Let's see what happens if we take the name out. I'll save it. And we get an error. I'll increase the size here. If ngModel is used within a form tag, either the name attribute must be set, or the form control must be defined as stand alone. So for Angular to work with a field, you need to remember to add that Name attribute. And the form works again fine.
Shutting Off Browser Form Validation
One of the things we want to do when we're working with forms is we want to turn off browser validation. There's going to be a whole module on validation, but it's important to get this set up up front, in case you run into it. Let's say our input field, our text field, is required. Required is a valid HTML attribute. So, I'll save this. And I won't enter a name, but I'll click the OK button. The browser pops up this warning message. Please fill out this field. Now, this message if going to be different on every browser. Like here we can see it went away automatically. Let's see what that same message looks like on Edge. So here's an Edge browser window, and I'll click OK without entering a name, and we get a very different experience. The name is surrounded in red, and the message doesn't have that cool look that you get in Chrome. And this is referred to as browser validation. It's done by the browser itself, and you have no control over how it looks, or how it behaves. So we'll see later in this course how to get control over that. But for now, we want to shut off this browser validation. We go to the form tag, and we add an attribute called novalidate. I'll save it. And now when we hit the OK field without entering a name, we don't get the message from the browser. It still doesn't do anything, but at least later on in this course, we'll get control over how to actually show a validation error. And this is important, because most likely, the users of your application are going to be looking at it in different browsers, and you want to give them all the same experience. So, remember to turn off browser validation by specifying the novalidate attribute directly on the form. And this is part of the HTML standard, it's not an Angular 2 feature.
Styling Forms and Text Input Using Bootstrap
So we have some basic forms working, but before we go too far, I just want to make sure our forms are looking good. So I'm here at getbootstrap.com, and we'll use Bootstrap to style our forms. I'll click on the Getting Started link, and if we scroll down a bit, we can see the Bootstrap CDN section. I'm not interested in a theme, or the JavaScript part of Bootstrap, just the CSS. So, I'll copy this link right here. Now, there are lots of ways to get Bootstrap into our application. One would be through Bower or NPM, and then it would have to tie it in with our package manager, whether it's Webpack, System JS, Browser FI, and there are a lot of options. But, in order to get started quickly, I'll just take this link and paste it into our index.html page. And I'll just paste it as a link at the bottom of our head section. So now we'll get Bootstrap directly off the internet from Bootstrap CDN. Now, in order to use Bootstrap, let's go to our home.html page. And the first thing we want to do is to warp our form and its header inside of a container. So that should give us some simple styling now. I'll save these two files. And the browser refreshes, and you can see that we have our Ariel font and our form has a margin too. Let's look into styling the rest of the form. For the button we can add some Bootstrap classes. Btn will give it regular button styling. And we can say btn primary to give the button emphasis. So we have a Bootstrap styled button. I'll take this form.pristine tag out, and let's also style our text field. We can wrap it and its label in a div. We'll give it a class of form group. And we'll add a label here. I'll take out the placeholder. The name will be First Name, and we'll also give our input a class of form control. And I'll save it, we'll see how this looks. So there we have it. A form control takes up the full width of the viewport. And that's the kind of styling we get with Bootstrap. And because we're using a form group, we'll have our OK button on its own line now. One thing I should probably mention is it's standard in HTML to add a for attribute to a label. And this will assign the label to the input pointed to by for. So, you would put, basically, your firstName ID here, and then the input tag would have an ID itself, called firstNameID. And that simply links up the label with the input. If you were to touch or click on the label, that input would get focus. But, more commonly, this is just left out, and I'm going to leave it out as well. To get focus on a field, you can just tap or click on the field. There's already enough going on with the input, in that we're giving it a name right here. And also, needing to give every field an ID is just too much unless you absolutely need it. So, now that we have this, let's also quickly create a Last Name field. So now we have our First and Last Name fields, styled with Bootstrap.
Checkboxes
Let's look at adding a checkbox to our form. The first thing I'll do is I'll rename this to Employee Form. And throughout this course we'll be building up this form. So, after our First and Last name, let's add the checkbox. Now to style this in Bootstrap, we need to wrap everything in a div, and give it a class of checkbox. Then we create a label tag, and within that we have our input tag, with the type of checkbox. And right after that, we can place whatever text we want to show. So if this checkbox is toggled on, it'll be a full time employee. I'll save it, and we get our Bootstrap styled checkbox. Now, just like we saw with the Name field, Angular is not going to be aware of this checkbox. We need to give it a name, and specify the ngModel directive. So I'll name it Is Full Time, and I'll specify ngModel. And just remember that that name is required. If I leave it off, let's see what happens in the console. We get an error. And we saw this before. If ngModel is used within a form tag, either the Name attribute must be set, or the form control must be defined as stand alone. Just make sure that if you're going to use Angular to access this field, or to validate it, or to use it in a posting, we need to have that Name attribute there, along with ngModel.
Radio Buttons
Let's add some radio buttons to our form. Radio buttons are created in a similar way to checkboxes. I'll create a div with a class of radio. I'll create the label tag. And an input tag with a type of radio. In addition to the type, we're also going to need to give it a name. The name is what groups different radio buttons together. And I'll just give it the name Pay Type. And we're also going to need to give it a value. The value is what'll get posted when we post the form. So, I'll give it a value of W2, and this will be a W2 pay type. And if you're not familiar with W2, that's a way that employees get paid in the United States. They're generally either a W2 employee, or a 1099, which is another way of handling taxes for a contractor or part time worker. So, we'll add 1099 as another radio button. We'll change the value to 1099, and we'll change the actual label itself as well. Maybe we want to default to W2, so let's add the Checked attribute, and I'll save this. So now we can toggle between 1099 and W2. That's working fine. One thing we want to do is add some space and a header above W2 and 1099, that way it doesn't run into our checkbox. To add a label for our radio buttons, we can add that right here, above our first radio button. I'll just create a label, and we'll call it Payment Type. So that shows up fine right here.
Select and Options
Let's add a select form control with options. The HTML is going to look a little bit like text. So, let's copy one of those. And we'll add it after the radio buttons. So, a select needs to be wrapped in a div. And we'll give it the class form group. We'll give it a label of Primary Language. And instead of the input, we're going to need a select with options. We'll make sure that the select has a class of form control. So we have some options. If we look at this now, we can see that we get the proper styling. We have our Primary Language. Defaults to the first one, English. And we can click on the drop-down and select any one of our options. A lot of times, we're not going to hard code our options like this. In fact, let's take these out, and let's use ngFor. Remember we need an asterisk before we put ngFor. And if you're not familiar with ngFor, this is a repeater. It'll repeat this same dom element over and over. We just need to create some kind of loop for it. So we'll use the es6 let statement. Let lang of languages. And we'll have to create languages on our component. And this is good enough to get a loop happening. Now, lang is going to be a local variable to this template. So we can just go ahead and use that as the text for our option. And we'll create languages. We'll go to home.ts, the type script component. And we'll create a languages public member. So, just to look at this in more detail again, we have our select tag. And we want to set up our options. So we use ngFor, which will repeat options over and over. While this loop is occurring. Lang will become a variable within this template. And languages is gotten from the component. So, for each member of the array languages, a new options will be stamped out, containing that language. I'll save this. And just make sure this refreshes. You can see that the drop-down for the select works exactly the same way. So, sometimes you'll see the options hard coded here, or sometimes you'll see them hard coded back in our type script component, right here. But, in many other instances, these are going to be loaded from some kind of data service. And we won't look at that now, but we will take a look at that when we start dealing with HTTP, and posting forms, and accessing a server.
Browser Inconsistencies for Input Types
I'm here at the Mozilla Developers Network. And I want to take a look at the input tag, especially the Type attribute, in more detail. Here's our input tag, and if we look down a little bit, we can see the Type attribute. And we've been working with checkboxes, and radio for radio buttons. And of course, we've been working with text, right here. But, as you can see, there are various other inputs that we can use. And the main problem with using these is inconsistency among the browsers. I'll scroll up a bit, and let's take a look at the date field right here. Let's add this to our form, but we'll see how it behaves in different browsers. Again, I'll just copy the First Name field. I'll go down to the bottom, and I'll paste it and we'll make a date out of it. Let's just call it Date Hired, and type will be date. We'll take out this other information for now. I'll save it and let's see what we get. So, we have Date Hired. It's looking for a month, day and year. And we can go ahead and type in the values, or we have a drop-down calendar. And that all works fine on Chrome, but let's see what it looks like on another browser. I have Edge from Microsoft up, so let's look at that one. So we have our Employee Form in Edge, and you can see here the Date Hired looks much different. Let's look at them side by side. So, first of all, the alignment's off. Next, I click on the field, and you can see that the interface is much different. It's almost as if it was meant for mobile. And again, looking at the Chrome interface for date/time, we get the calendar month. Now, which one is better doesn't really matter. The point is your users are going to be getting different experiences from different browsers. So, in cases like this, when using dates, it's better to use third-party controls which will give you consistency across the various browsers. And we'll see that later in the course. And just to check this out again, let's also just play around with the color type. Color is another standard HTML5 type for an input. On Chrome, we get a big, black color to start out with. And we get a color dialog. And then Edge, let's refresh this, and we don't see anything. Color doesn't work at all in Edge. So, again, whenever you use these different types for the input tag, make sure they work properly on the browsers that you're using.
Summary
We started off this module by placing a very simple form inside the Angular seed project. We saw how Angular automatically placed an ng form directive on our form. We were able to make our form controls known to Angular by placing an ng model directive on the main tag of each control. This is usually the input tag, or possibly the select or other tags. We shut off browser validation using the no validate attribute on the form tag. Then we looked at styling our form using Bootstrap CSS classes. We added checkboxes and radio buttons to our form, and styled them with Bootstrap. Then we looked at using a select control with options. We hardcoded options directly into the form's template. And we also saw how to use Angular's ngFor directive to specify options. Finally, we saw how several input types create inconsistent experiences for users across browsers. We'll say more about this and fix it later in this course when we cover third party controls. But, for now, just beware of which input types you use. Test them in all the major browsers before using them in a final project. So, now that we can create simple forms, we'll see how we can move data into and out of our forms with data binding. We'll cover that in the next course module.
Data Binding in Angular Forms
Introduction
Welcome to this module titled, Data Binding in Angular Forms. My name is Mark Zamoyta. As software developers, we need to be in control of our data. Data binding within a form needs to be understood well, because this data will almost always get posted to a server for storage. Sometimes this data includes highly secure information, such as Social Security Numbers, passwords, or credit card information. So you'll need to understand how data binding is performed before sending you customer's information over the wire. We'll start off this module by seeing the one-way and two-way data binding syntax in Angular 2. This is done with the ngModel directive, which we'll be seeing a lot of in this course. We can bind our data to our own JavaScript or TypeScript object. Usually referred to as a model. Angular will also automatically create its own model for us. And we'll see how we can view it and work with it as well. Next, we'll start up the form project from earlier in this course. And we'll add a new employee model for us to work with. Throughout this course module, we'll see how our employee model compares with Angular's automatically generated model. We'll bind our text fields to our new model, using two-way data binding. Then we'll see how we can call a method on our component during data binding. Calling a method gives us a chance to execute any business logic, data access, or other code as a form gets filled out. We'll see how to bind a checkbox to a Boolean value. We'll bind radio buttons. And we'll bind a select control with options. By the end of this module, you'll understand one-way and two-way data binding in Angular 2. You'll see how forms mostly use two-way data binding and you'll have an option of using your own model to hold data or using Angular's automatically generated model. So let's get started and check out Angular's data binding syntax.
Data Binding and ngModel
Let's take a look at data binding and ngModel. ngModel is a directive that almost always gets placed on a form's field that we're interested in controlling. Let's take a look at how ngModel actually gets used. So here's an example of no actual data binding going on. We have an input field and we give it a name, a first name. And we specify the ngModel directive. In this case, there's no data binding going on. However, because the directive is there, Angular will be aware of this field. And it will use this field to build up its own internal object. This object will have a property called "firstname". Because ngModel does require this name attribute to exist, so that it can build its own object. But as far as binding to our component goes, nothing will happen if we just leave the directive like this. Now in this example, we have one-way data binding. Again, we have an input with the name of "firstname", but now we specify ngModel in square brackets. So this is a property binding syntax. And we're setting ngModel to "firstName". So what this will do is it will look for a property of "firstName" on the TypeScript component and initialize this input field to "firstName", whatever that value is. But as the form is filled out and the "firstName" changes, that won't reflect back in the component. This is only one-way data binding. Now two-way data binding is similar to the last example, except now we're going to handle the ngModelChange event. You can see here ngModelChange is in parenthesis, showing that it's an event. And when this fires, when the input field changes, "firstName" will be set to $event, which happens to be the value in this case for the input. So now we have two-way data binding. When the form is initialized, we'll grab "firstName" off the component and populate the form with it. And then as that "firstName" changes, we'll set "firstName" in the component to the input field's value. And this is two-way data binding. However, there is a simpler syntax for this. It's referred to, in other Pluralsight courses, as a banana in a box syntax. We can merge the ngModel property and the event for ngModelChange into this syntax right here. It's called banana in a box, because you can think of the parenthesis being a banana and the square brackets being a box. And by setting that to "firstName", we get two-way data binding. And that's what we'll use most often in our forms. It's a short, compact syntax. And we just need to specify the model that we're going to use to hold the value. So "firstName" needs to exist on the component. In the rest of this course module, we'll take a look at using these various syntaxes for ngModel.
Creating a Model Object
Let's add a model to the component that we use as a form. The first thing I did was I commented out the bulk of the form. For now, let's just work with the first field. The "firstName" and we'll data bind that. But to create a model, let's make a subfolder underneath app. And I'll just name it models. And within models, I'll add our employee models. I'll go to New File, we'll call it employee.model.ts. It's a TypeScript file. So let's just make a plain TypeScript class. And I'll export it. We'll call it Employee. And for now, we just need a constructor. And we'll keep things simple for now. We'll just create two public fields for firstName and lastName. So we'll be adding more fields to Employee as we data bind our entire form. But for now, we'll have a public property called firstName and a public property called lastName. If you're not familiar with this syntax, TypeScript lets you define public properties directly in the constructor by using the public keyword. They could also be specified as private. So we have our Employee class and remember that it's in this models folder. Let's go back to home.ts, which is our component. And we'll add a model. We'll set it to a new Employee. And we'll just pass it a first and last name. And we'll need to import Employee. We have to go up a directory into models. And the filename is employee.model. So now we can perform our data binding directly on this model property. And we'll take a look at how to do that in the next video.
Binding to Your Model
Our model is all set up, so let's take a look at our template. So you'll notice right now, there's no ngModel. There's no binding or ngModel directive whatsoever. And we'll take this step by step. The first thing we want to do is take a look at our model. And we can one-way bind to it here with curly braces. So you can see, we are getting a "firstName" of "Darla" and a "lastName" of "Smith". And as long as we're looking at models, let's also add a break. And we'll look at Angular's value. We'll look at our template reference variable form and we'll look at the value property. This is what Angular maintains for us. As far as keeping track of the model. So we can see that our data model has a "firstName" and "lastName". But Angular just doesn't see it, and that's because we're not using ngModel yet, so let's do that. We'll add the ngModel directive. So now Angular does see "firstName", but it's empty. By using ngModel alone without specifying any kind of data binding, Angular does become aware of the value for "firstName". Let's change the "firstName" to let's say "Bob". And we can see that Angular is aware of that. But our model isn't, our model still points to "Darla". So let's apply a one-way data binding. We'll use square brackets and I'll put this on the next line for readability. And we'll assign it, model.firstName. I'll save it. And now we can see some data binding taking place that makes more sense. Our model "firstName" is "Darla". And that's what appears in the initialized field. So that's good. And Angular is also aware of it. It has a "firstName" of "Darla" as well. But what happens if we change this. I changed it to "Daphne" and Angular is aware of that change, because we're using ngModel. However, our model still has its "firstName" set to "Darla". It's not picking up the change. That's because we're only doing one-way data binding. We need to do two-way data binding to get everything to work properly. So I'll add the banana in a box syntax. So we have parenthesis inside square brackets now. And now everything's working fine. Our model and Angular's internal model are in sync. The field is initialized to "Darla". And when I change it back to "Daphne". The model, as well as Angular's view of it are also in sync. Now just to check things out as we saw in the prior video, let's use the longer syntax, just to make sure this works. We'll bind our model to this input to initialize it. But then on changes, we'll call the change event ngModelChange. And that's in parenthesis, because it's an event. And we'll say "model.firstName=$event". That'll be the value of this input field. I'll save it. And I'll change "Darla" to "Daphne". And our two-way data binding is working fine. In the next video, we'll take a look at actually using a function here. Rather than setting "model.firstName". That way we can execute some code as the value changes. And possibly change that value ourselves.
Calling a Method in Place of Binding
Let's take a look at calling a function rather than directly setting a property on the model. We can't use the banana in a box syntax, that won't let us call a function. So we'll use the syntax right here. We use ngModel as a property with the square brackets. That'll be one-way data binding from the component into this field. And we'll also use ngModelChange as an event with parenthesis. And we'll just directly call a function here that we'll create. Let's call the function, "firstNameToUpperCase" and we'll pass this $event, which will be the value of this input field. And I'll go to our component, home.ts. And let's create that function. And value is a string. So first let's check and make sure that we have a value, so that we can work with it. If the length is one or more, we can go ahead and set this.model.firstName. We want to grab the first character of value and convert it to uppercase. So we'll call charAt(0) and we'll call toUpperCase on it. And we'll add the rest of the string, if there is one. That'll be value.slice(1). And we just also want to make sure that if the length is zero, we still set this.model.firstName to an empty string, which will be value as well. So I'll save this. And I'll erase "Darla". And let's just add "Daphne", but I'm going to type a lower D. So I typed a lowercase D and it came up as uppercase. And that's working fine. No matter what we type in this field, if we enter a lowercase value, it'll show up as uppercase. Only for the first character. So that might be quite an arbitrary rule. And there probably are first names that begin with lowercase letters. But the key point is to show off how we can call this function, "firstNameToUpperCase". Or call any function when we're doing two-way data binding. We just have to make sure we're not using the banana in a box syntax and that we are handling this ngModelChange event.
Binding a Checkbox
Let's take a look at data binding to our checkbox. And just as a quick note before we look at the checkbox, you can see that I added in the Last Name field. And this is set up exactly as the First Name field. We have the banana in a box syntax for ngModel and instead of firstName, we're setting it to model.lastName. So let's uncomment our checkbox. Earlier in the course, we set up ngModel. But let's use two-way data binding now. So we need to add a new field on our model, we'll just call it, isFullTime. So I'll save this and we'll go to our employee file. Employee.model. isFullTime will be Boolean. True or false. And we want to make sure that we construct this properly as well. We'll pass in true to our constructor. And while we're here, let's also remove this function that we used for experimenting. So everything's saved, let's see if our form is working right. isFullTime is set to true for the model and within Angular, that's good. And our checkbox is checked. Let's uncheck it and everything goes false, so that's fine. So binding to a checkbox is very simple. We can use the same syntax we used for text fields. And we just had to make sure that we're binding to a Boolean value.
Binding Radio Buttons
Let's look at data binding with radio buttons. So I'm looking at our template for our form and here we have this Payment Type section which has two radio buttons. I'll just uncomment this. And we can see there's no ngModel on this now, but let's add one, so we can two-way data bind. We're going to need to create a field and let's call it paymentType. And while we're at it, we can fix this too. We'll give it a name of "paymentType". And I'll copy this ngModel attribute and we'll use that on the other radio button as well. So remember that the names are the same for radio buttons. And that's how we can do two-way data binding. No matter which radio button is pressed, it's still using the same name. You just have to make sure that ngModel is specified on each button. So this paymentType will include the value "w2" for this radio button or a "1099" for this button. Let's go ahead and add this to our model. It's called "paymentType". And it's a string. We'll add it here as well. We'll have the default be 'w2'. And let's see what it looks like in the form. So I'll save this. And let's take a look at the form. We've got our "paymentType" of "w2". So that's fine. And if we click on the radio button for 1099, everything changes. That's great. So this is working fine. The key thing to keep in mind when working with radio buttons is each button in the group needs to have the same name. Here it's "paymentType". And all of the ngModel attributes are going to be the same for each button.
Binding a Select Control
Let's take a look at data binding to our select control with options. We've already created a select control, so I'll just uncomment it. This was done in the prior module. And here is our select tag. So to work with ngModel, let's give it a name of course. And we'll call it "primaryLanguage". And we'll specify the banana in a box, ngModel attribute. And we'll set that to "model.primaryLanguage". And just to review the options, we're looking at our languages property on the component. And we'll loop through that, specifying a lang variable, which will become the text of the option right here. So we just need to make sure that "model.primaryLanguage" does exist. So let's go to our model. I'll go to the models folder and click on employee.model.ts. And we'll add that. It'll be a string. So I'll make sure the model gets saved and we'll look at our home.ts file. This is our component. And we get the red squigglies, because Employee is invalid now. So we'll add 'English' as the primaryLanguage. Alright, so let's make sure everything saved. I didn't save the HTML template yet, so I'll save that. We get the refresh and we can see "primaryLanguage" is showing up. Both in our model and Angular's model as well. If we change the language to say Spanish. That changes in our debug output as well. So everything's working fine. There's a lot more we could do with select controls, but that becomes more of a validation issue and we'll look at validation a lot more in the next module. So let's wrap up this module with the summary.
Summary
In this module, we saw how to work with data binding in Angular 2 forms. We saw the various ways of working with the ngModel directive for one-way and two-way data binding. We created our own employee model to hold our employee information as it was filled out within a form. We compared this to Angular's own automatically generated model throughout these clips. We performed data binding for text inputs. Checkboxes. Radio buttons. And select controls. Along the way, we also saw how to call a method when binding from a form control to our model. This could be used for advanced processing by our form, since we could execute any code at this point.
Form Validation
Introduction
Hello. My name is Mark Zamoyta, and welcome to this module on Form Validation. As a user fills out a form, we need to decide when and how to display error messages. Very often, we'll have required fields in a form, or we'll have more complex rules for a field that require a regular expression to validate. In this module, we'll see how validation works in template-driven forms. We'll also take a look at helping out our users, when possible, by giving them clear error messages at the right time. We'll start this module by looking at CSS Classes that Angular uses for form validation. These classes are placed on fields automatically, as the user fills out a form. These same CSS classes also exist as properties on the ngModel instance. We'll look at how we can access these properties in a template, and create expressions with them, to show and hide error messages. Next, we'll look at styling controls and labels when a field is invalid, or has some other error. We'll use bootstrap classes for this. We'll look at HTML 5 attributes that allow us to validate fields. These include required, minlength, maxlength, and pattern, for regular expressions. Then, we'll look at styling a select control. We'll have to go in-depth to work with selects, and we'll need to bind to blur and change events. We'll see techniques that can be used to validate any control, by using custom functions on a control's events. We'll look at properties placed directly on the ngForm instance, which can be used for form validation. Just as validation properties get placed on fields, these same properties get placed on ngForm. We'll see how to work with these properties at the form level. By the end of this module, you'll be able to detect errors as a user fills out a form. You'll be able to apply styling and classes to labels and fields, in order to give your users a helpful experience. So, let's get started learning about form validation.
CSS Classes for Validation
Let's take a look at validation classes in Angular 2. By validation classes, we mean real CSS classes that Angular is going to attach to a field on the form. Once a field has a class, we're able to style that field as a developer. But, for now, let's just take a look at what these classes are. When a form is first created, all the fields are marked as ng-untouched. And, this is the CSS class that gets attached to each field. And, as we tab off of fields, the class changes to ng-touched, meaning that the end user actually had a chance to enter information, and whether they did or not, they still left that field. So now, the field is labeled touched. There are two other classes: ng-pristine, and ng-dirty, which monitor whether or not the user actually entered any information, or left the field in its initial state. So, pristine is the starting value. Pristine means it hasn't been changed or touched. But, once the user changes something, it immediately gets set to ng-dirty. And, also, for these classes, such as ng-touched or ng-dirty, once these are applied, they're applied until you reset the form. And, there's one final set of classes. There's an ng-valid, which tells us whether or not the field is valid. And, there's also an ng-invalid class. So, depending upon how you set up your validation, right from the get-go, it might be set as ng-valid. Or, if you have something like a required field, that would be set to ng-invalid. So, let's see how these work in a real form.
CSS Validation Classes in Action
Let's take a look at these CSS classes in an actual form now. I took the form from the last module, and I commented most of it out. The only thing we need to work with is one field. So, we have the First Name field right here, and that's displaying fine. So, in order to view which classes are attached to our input tag, let's just write out some information. We're going to need to access our input tag, and we can do that with the template reference variable. We'll call it firstName, and we'll access the className property. And, we have to attach that template reference variable right here, to the input tag. So, I'll save this, and we'll get the refresh. And, we can see the classes that are currently attached to input. First, we have form-control, and, that's the bootstrap class that we added right here. Next, we have our Angular added classes. Let's take these one by one. First, we'll look at ng-untouched, and its companion, ng-touched. If we give focus to the field, we can see that it still has an ng-untouched, and if we enter some kind of value into the field, it also remains untouched. And, this changes only when the field loses focus. So, let me tab out of the field, and we can see that ng-untouched was removed, and now we have ng-touched added as a class to the field. So, whether something is untouched or touched, has nothing to do with data entered into the field; it has to do with whether or not the field lost focus. So, I'll refresh this. Let's look at the next one, ng-pristine, and its companion, ng-dirty. Right now, the field is pristine; none of the data has ever been changed in it. If I go to the field and type a value, we can see that ng-pristine was removed, and it was replaced with ng-dirty. Now, the field's dirty, and the model is dirty. If we delete the value, we can see that it still remains dirty. So, there's no mechanism to test whether or not the form is in its initial state. Once a field becomes dirty, it stays dirty. So, I'll refresh this again, and let's look at the third set of CSS classes. We have ng-valid, and that's going to stay valid, so, let's actually go to our markup, and, let's make this input field required. So, now our field is invalid. We have the ng-invalid class added to it. If we enter a value, we can see that the class is changed to ng-valid. If we delete the value, it returns to being ng-invalid. So, these three sets of classes are completely handled by Angular. And, you can go ahead and style off these classes, but, it's actually a lot easier to work, not with these classes, but with these same concepts as properties. And, we can get access to these classes as properties that we can program with. And, we'll see that in the next video.
ngModel Validation Properties
So, we've seen the ng dash classes, the CSS classes, that Angular attaches to fields for validation. But, as a developer, it's a lot easier, sometimes, to just work with properties on ngModel, rather than work with CSS classes. So, let's take a look at the classes again, and how they relate up to the model properties. We have ng-untouched and ng-touched, and as far as the properties go, we have untouched and touched. So, we simply remove the ng dash from the CSS class name to get the properties. Likewise, we have ng-pristine and ng-dirty, and the associated properties on ngModel will be pristine and dirty. And, finally, for ng-valid and ng-invalid, we simply have valid and invalid properties now. And, if there's any trick to this, it's making sure that we're working with ngModel properties, and we're not trying to access properties of the actual dom object. So, let's take a look at that. Instead of looking at the ng classes that Angular adds, let's now take a look at the properties that go on ngModel. So, I have the example from our last demo, and if we look at our input tag, we see the template reference variable firstName. So, what exactly does this reference? In the last demo we did, we saw that firstName referenced the input tag itself. And, that's fine; we needed to pick up the CSS classes from input. But, now, we need the template reference variable to access something else. We need it to access ngModel. And, you can see that we are doing two-way data binding here. We did this earlier in the course. And, that's fine, but, how do we access this ngModel class that Angular is going to instantiate for us? Well, we can just go ahead and assign ngModel to a template reference variable. And, it's as simple as that. This ngModel directive gets exported as the string ngModel. And, that's how we can pick it up, and assign it to firstName, our template reference variable. Now, we still have this required attribute, so, that's good; let's take a look at that. We'll take a look at first name dot invalid. Remember, for the properties coming off of ngModel, we don't use the ng or the ng dash prefix. So, we're just going to write out firstName.Invalid, which should be true, because we have the required attribute set. I'll save it, and we'll get the refresh. And, it does come up as true. As we enter a value into firstName, invalid is now false. And, for each one of the six classes that we saw earlier, we can also access those same classes as properties here. So, let's take a look at our dirty flag. When we start off, I'll wait for the refresh, and we start off the value set to false, it's not dirty. And, as I enter a value, firstName.dirty becomes true. And, I won't run through all these flags, but, you do have access to them. And, one of the benefits of using these properties over the classes, is that we can use those in conditional expressions. And, it's a lot easier doing that than pulling out the classes, and checking if classes exist. So, in the next video, we'll actually do some styling with these properties. We can get error messages to show up in the form, and we can do things, such as, change borders and colors, depending upon the state of the control.
Showing Error Messages
I'm here at the Bootstrap homepage, "getbootstrap.com." We want to work with these properties we have now, that check for various states of validation for our control. And, one thing we can do with these properties, is use them to give some kind of error message for a text field. I'll click on the Components link, and, if we scroll down the list on the right, here, we can see alerts. So, what I'd like to do is, I'd like to take this error alert, right here, the red one, and, we can see the markup for it right here; it's alert alert-danger are the classes we need. And, would like to add this to the field if it's invalid. And, this would be our way of showing the user that something's wrong. Now, we're not always going to want to show this; we only want to show it under certain conditions. And, that's what we'll experiment with this video. Remember, the classes we need are alert, and alert-danger. So, here's our project from the last video. I'll just remove our debug information. And, let's add our new div right after the input field. And, the classes we need, again, are alert-danger. And, we'll just say First Name is required. So, if we save this, we'll see what it looks like, but, we're going to need to put some kind of conditional statement in there. And, the error looks good. So, let's only show this under certain circumstances. And, we can add a node to the dom or take it away by using the ngIf directive. Remember to use the asterisk before ngIf, because this is a structural directive that'll change the structure of our dom. So, when we set ngIf, we can set it to any expression. And, the first expression we'll try out is firstName, our template reference variable, dot invalid. That way, if the field is invalid, we will show this error message. So, I'll save this, and, I'll make sure this refreshes here. And, we get our error showing up, because, at the start of the form, the First Name will be invalid; we haven't entered anything. If we enter a value in there, we can see the error goes away. If we delete the value, we get our error back. So, this is working; we're getting our div and we're removing it at the proper times, but, it's generally not a good idea to start throwing a bunch of errors at your user. You want to give them a chance to, at least, tap through the form, or do some kind of modifications to the form before you give them error messages. So, we can just alter our expression, instead of just firstName.Invalid. And, we can also specify that the field must be dirty, to show the error message. So, I'll save it. So, now that the field is pristine, we don't get the error message. If we go in, and we start typing, we're fine, but, then, when we delete it, it'll have a dirty state, and we get our error message again, and that's fine. Some designers and developers don't like to show any error messages until after the entire form is submitted. And, we'll see how to do that later in this module. But, for now, this is a pretty valid way to go. We could also modify the condition and say, we'll show this div if the firstName is invalid, and the firstName is also touched. Let's try this out. So, we start off without the error message. I'll give it focus and then I'll tab out of it. And, we get our error message. So, if someone tabs over a field, you have the option of giving them a message, right at that point. And, these expressions for ngIf can be as complex as you'd like. You can access template reference variables, and, you can also access other variables, directly from your Type Script class. In this case, it would be our home.Ts file. In addition to showing an error message like this, we also might want to modify the CSS for the firstName and its text field. We could turn it red. We could outline it. If you've done the Tour of Heroes tutorial, they added a red border to the left side of the field, to show that there was an error. So, the next video, we'll take a look at doing that; working with CSS and classes, in order to better style the form.
Styling Controls for Errors
We have an error message showing up now, but, let's also style the label and the field itself. I'm here at "getbootstrap.com," Bootstrap's home page, and I'll click on CSS, and, I'll go to the section called Forms, and, I'll click on Validation states. I'll zoom in a bit here, and, we can see this input with error styling. The label's red and the field, itself, has a red border. Let's add that to our own input control. I'll scroll down, and we can see the markup for it here. We have a div, and the div encompasses both the label and the input, and, it has a class on it called has-error. So, we just need to add this class to our own field. But, we need to add it conditionally; we don't want to show it all the time, of course; only when the field is an error. One other thing to take note of is that the label has this class called control-label, and we've had no need to use that up until this point, but, to get the label styled properly, we need to add this class. So, I'll add has-error to the div, and control-label to the label. Looking back at our First Name field, here's the div that encompasses the label and the input. And, just to make sure everything shows up okay, let's add the has-error class. And, our label needs a class: control-label. I'll refresh it, and, we get our error styling properly; the label and the field itself show up in red. So, the trick is, how do we add this class, has-error, how do we add it conditionally? You want to use the same condition that we used in our if down here, our ngIf. I'll just copy out that condition, and we'll go back up to the div, and, there's a special binding we can use. We can, actually, bind to class dot classname, and the class name, you remember, is has-error. So, Angular will see this as a property; it's in the square brackets, class dot class name. And, it will add this class or remove it, as needed, depending upon the condition that we set. And, I'll just paste in the condition from our ngIf. So, if the firstName is invalid, and the firstName has been touched, has-error will be added to this div. And, if this resolves to false, has-error will be removed from the div. Let's save this and see the behavior. So, our form looks fine right now. Let's not enter anything and I'll tab out of it. Tabbed out, and we get our error message, and we get our error styling; that's correct. Let me go back, and I'll enter a value. I'll tab out of it. And, everything's fine. If I start to delete this value, we can see our styling comes back, along with our error message, so, that's good. So, the important thing to remember, is that we can easily add a class, or remove a class, from a dom element, by using this binding. It's a property binding, so you use the square brackets, and you specify class dot class name. In this case, we're using the bootstrap class name called has-error. If the expression evaluates to true, the class will be added to the element, and, if it evaluates to false, the class will be removed from the element.
HTML 5 Attributes for Validation
Let's take a look at validation for a text input. We'll look at some of the attributes that we can use in the HTML markup. So far, we've been working with required, and, we've seen how that works. We can tab out of the field and we get our error message. But, there are some other common ones that get used. Let's try maxlength. I'll set a maximum length of three characters. I'll start to type a name, and it stops me at three characters. So, we're not getting an error; the browser, itself, is preventing us from entering any more data. And, that's fine. Some browsers might work in a different way, but, for our case, this is good. Now, certain browsers also implement a minlegth, a minimum length, and Chrome is one of them. So, I'll save this, and I'll start to type a name, but, I'll leave it at two characters. And, I'll tab out, and, we get our error message. Now, our error message isn't much use to us; "First Name is required." So, we would have to fix this up, as we add validation to our field. So, I'll try it again, and we get our message. But, the problem is that minlegth isn't widely accepted. It's not an official HTML 5 attribute for an input. I'm here at caniuse.com, and let's take a look at minlength. If we look at the chart, we can see that it's fine in Chrome, and the latest version of Firefox, but, for things like IE and Edge and Safari, it's not going to work. And, we can compare that to maxlength. That's implemented, pretty much, across the boards. So, another option we have is, we can specify a regular expression for validation. Let's do that. The attribute we use is pattern, and let's just set a simple pattern to start with. We'll make sure that the first name begins with a Q, just an arbitrary rule for testing. A refresh. And, let's try typing a Z. And, I'll tab out of it. We get our error message; it doesn't begin with a Q. I'll type Quenton, and, we still get our error message; that's because there's a problem with our regular expression. If we have just Q, we're fine. So, let's specify anything that follows Q dot star. And, now we can type Quenton, and we're fine. And, as we delete it, we'll delete the Q, type a Z, and we get our error. Now, we saw earlier that minlength wasn't supported, and, we can go ahead, and add our regular expression for that. You can put, let's say, a minlength of three. So, we'll put three characters, and, the last character will accept one or more of those. So, I'll refresh it. And, let's see if it accepts a single character. I'll type a Z, and tab out, and we get our error. I'll type two characters, we still get our error. And, on the third, we're fine. So, regular expressions work well. You can actually search the Internet for regular expressions for email addresses, URLs, phone numbers, or anything. And, the pattern attribute is well supported for regular expressions. If we look at Can I Use, let's search for "pattern," we can see that it's accessible, pretty much, everywhere. So, Angular works well, in validation, when it comes to looking at the native attributes of an input field. We saw it work with minlength, maxlength, pattern, and, of course, required.
Validating a Select Control
The next thing we'll look at is styling a select tag. We added one of these earlier in the course, and, actually, commented out the entire form except for our primary language select and options. You can see, here, that we have a few options; we have English, Spanish, and Other, as a primary language. And, these options come from our component class. It's looking for languages. And, quickly, looking back at our component class, home.Ts. Here, we can see, our languages are already set up, as a public member. So, there are a lot of cases where you may not even need to style a select like this; it'll default to English, and that might be what you want. But, a lot of the times, you might see an option above this four loop here. Like, there'll be an option, telling us to select the primary language. And, this isn't a valid entry. We can set it as the default, explicitly; let's do that. And, now, we can see, when I save it and it refreshes, it'll default to Select a Language. Now, what if I don't select a language, and I just leave it there and I tab off? We don't get any kind of error message, and, there's no real mechanism within Angular to do that. So, we're going to have to handle this with Angular ourselves. And, this will give us some practice, working with event handlers, and also doing validation within our component class, itself. So, let's start off by doing this. We already added this Select a Language phrase. And, if they select that, or tab over it, we should validate it, and show an error message, if needed. Now, before we get into doing that, let's make sure that we're data binding our select. Let's do two-way data binding, with the banana in a box syntax. And, we'll bind to model.primaryLanguage. And, remember, whenever we use ngModel, we need to have a name for the item. So, let's add a name; primaryLanguage. So, let's just go over the select, and make sure everything's right. We have a class form-control. The name is primaryLanguage. We're doing our two-way data binding. We're going to have to set up this model, with the Primary Language field. And, we should, actually, instead of option default, let's just give it a value of default. And, that'll make it easier to set up our data binding. And, then, for options, we're going to loop through all of the languages in the array. Alright, so, let's create model.primaryLanguage, right here. We have our models folder, and, we'll add public primaryLanguage as a string. We'll go to our home.Ts file, and we'll just add default. If you're coding along with me, there may already be a value here from the last module. Just make sure the fifth argument is default, as you see here. So, now, I save it, and we get the refresh, and it defaults to Select a Language, and, that's what we want. Still, we haven't done anything about our tabbing problem. If we tab off this now, we want to show an error message. So, let's go back to our template. One thing we can do, is conditionally add that has-error class, that we saw in the last video. We'll bind to class.has-error, but, what do we want the expression to be? Angular really isn't going to do anything for us as far as building an expression here. We're going to have to build one out, ourselves. Let's create a variable in our component class, and, let's just call it hasPrimaryLanguageError. So, if this flag is set to true, we'll get our styling for the error, with the bootstrap has-error class. So, I'll just save this. We'll go back to our component class, and, we'll set hasPrimaryLanguageError to false. So, we're starting to build a mechanism, but it doesn't do much yet. I saved it, and I refreshed, and, there are no errors, so that's good. But, the next thing we need to do, is, now that we have this flag, and we know we can set it within our code, let's actually bind to an event. I'll make some space here, and let's bind to the blur event. The blur event fires when a field loses focus. So, whenever the select loses focus, we want to call a method. And, let's just call validatePrimaryLanguage. And, if we save this now and try it out, we should get an error, because, validatePrimaryLanguage doesn't exist yet. I'll tab off of it, and yep, we get our error. So, we'll create the method validatePrimaryLanguage. And, we're going to want to pass at the event too. Let's make sure that's happening. We can specify dollar event. And, just to see what's going on, let's just log out the Primary Language from our model. So, I'll just select English and I'll tab out of it, and, we get English, as our primary language. If we go to Select a Language, and we tab out of it, we get Default. So, let's take advantage of this, and, knowing that we have Default set up, as a string, let's set an error if we get that. If this.model.primaryLanguage is identically equal to default, we'll set this.hasPrimaryLanguageError to true. Otherwise, we'll just set it to false. So, let's give it focus, but, we won't change it, and I'll tab out, and we get our error styling; it's red. We just have to make sure that we link the label to it. Let's do that, quickly. The label needs the class control-label, and, that's from bootstrap, itself. So, let's try that again. I'll tab out, and it turns red. We could, just as easily, add an error message in there, as we did in the text field, in the prior videos, but, we already know how to do that, and, let's just focus on getting this validation to work for the select. If we go in, and we change it to English, and tab out, it gets cleaned up. But, what if we want to remove that error sooner? For example, I select English, right here, we want the error to go away at this point. Well, what we can do is, we can bind to the change event. And, we'll take a look at doing that, and a few other things, for validation, in the next video.
More on Select Control Validation
So, we're handling the blur event, but, we still have the problem where, when an error is shown, like it is now, if we select a valid option, such as Spanish, the error remains. So, there's something going on that we need to figure out. And, the main problem has to do with the data binding that we're using in our home.Ts file. That's the validate primaryLanguage method. There's no guarantee that our model is set up when the blur or the change event; we're going to be adding the change event soon. But, when those events fire, there's no guarantee that the model has been updated yet. So, instead of going against the model, let's just pass in the value here. So, if the passed in value is default, then, we'll set the error to true. Otherwise, we'll set the error to false. Going back to our markup, how do we get the value of this select? Well, we're going to have to create another template reference variable. Let's just call it primaryLanguage. And, you can see that I added the template reference variable right here; #primaryLanguage. And, we just want to make sure that that doesn't conflict with our model.primaryLanguage. And, it doesn't, because we're using model right here. So, now, in addition to the blur event, we also need to handle the change event. And, we can set it to that same method call. And, actually, we need to specify dot value to get the value of this select item. So, whether we get a blur event, losing focus, or we get a change event, these are change to the value, validatePrimaryLanguage will be called, with the proper value, of this select element. And, when validatePrimaryLanguage is called, we except the value, and we'll set the error accordingly. So, let's save this, and see if it works. We have Select a Language. I'll just tab out of it, and we get our error; that's good. I'll go back, and I'll select a valid option. And, it looks like I have a typo, so, we'll go back... And, I'll save it. We get our error. I'll select a valid option, and our error goes away. So, this appears to be working correctly now. If I Select a Language, we'll get our error immediately, and, that's probably what we want; we want them to actually select something that isn't that first item. So, that's how we can validate a select control. We had to create this template reference variable in order to access the value. We also could have accessed the value by using the event item, dollar event. We would have had to search through that for the target control, and accessed the dom that way. We want to try to avoid accessing the dom. And, we can do that, by using template reference variables. We handled the blur event, and the change event, and, there's one other problem that might come up, which is, what if the user completely skips over this select altogether, and they just hit the Ok button? Well, in that case, we're looking more at a general form validation before we submit it. And, we'll be covering that in the next module of this course. But, for now, just keep in mind, we do have this function, validatePrimaryLanguage, and we're probably going to need to call that, or check it, before we submit the form.
Form Level Validation
So far, we've been looking mainly at validation at the field level, for inputs of type text, or our select, in options. But, Angular also keeps track of validation at the form level. If you remember, earlier in the course, when we specified our form tag, we created a template reference variable called #form, and, we set it to ngForm. Now, you might be wondering where ngForm comes from. Well, whenever you have a form in Angular, Angular will automatically add a directive, called ngForm. And, that directive gets exported as the string, ngForm, and that's what we see here. So, we can access this ngForm directive, now, with our #form template reference variable. So, I'm back to having just a single firstName field. And, let's scroll down to the bottom, and after our Ok button, which is of type submit, this eventually will submit a form; we don't have that set up yet. But, let's just go ahead and use our template reference variable form, and, let's take a look at some of the validation properties that we had on fields. The same ones exist on the form, itself. So, let's look at pristine. And, I'll just wrap this, so we get it looking a little bit bigger. So, we get true for pristine. If we enter a field and enter some data, pristine becomes false, so, that's fine. And, let's also take a look at valid. You'll see we get true for valid. And, that's a little deceptive. Let's go up and look at our text field again. We specified this pattern from our previous video. But, for HTML 5, we also need to specify the required attribute. That way, the field is required, and the pattern will be checked. So, I'll save this, and it'll refresh. So, now, valid is false. And, we can go ahead and check any one of these six validation flags. Valid is valid, touched, untouched, and dirty and pristine.
Disabling the Submit Button
So, using these validation properties on the form itself, on the ngForm, they're mainly useful and things like a submit handler. If you are about to submit a form, you'd want to make sure it was valid, and that everything was good to go. But, one place where you do see things like this used, outside of a submit function, would be to disable a button, such as the submit button. So, let's do that. I'll make some room here. And, we can bind to the disabled property. Again, properties need these square braces, to perform a property binding. And, we'll just specify form.invalid. So, if the form is invalid, the button will be disabled. I'll remove this test message, and let's refresh. So, our Ok button is disabled. If I mouse over it, you can see we get the red circle and line through it. There's no way to submit the form this way. If I go in, and I enter a valid value, the button becomes enabled. Again, if I back up a bit, and I don't meet the requirements of a minimum length of three, we get the disabled button again. So, that's pretty much all we're going to cover in this module. We still need to cover submitting a form, and validation goes along with that, too. But, we're going to be covering that in the next module of this course, where we cover handling submit, making sure everything's good, and then, actually posting it to a server, and getting a response, whether successful or unsuccessful.
Summary
In this module, we learned how to validate forms and show error messages, when needed. We started by looking at the CSS classes, Angular places on fields, as a form is filled out. Ng-untouched and ng-touched let us know if a field has had focus, and then, lost focus. Ng-pristine lets us know that a field is in its original state, and its companion class, ng-dirty, lets us know a field's value has changed. Ng-valid and ng-invalid let us know a field's state, in relation to any rules we've set up for it. An invalid field usually results in an error. We also saw the ngModel property version of the CSS classes. We simply drop the ng prefix to get the property name on an ngModel instance. We use these properties to show or hide error messages, using ngIf. We also use these properties to add or remove classes from an element, using a special binding, class dot class name, with className being the actual CSS class name, to add or remove to an element. We saw several HTML 5 markup attributes used in field validation. These include required, minlength, maxlength, and pattern, for regular expressions. We looked, in depth, at validating select controls. We handled the blur and change events, in order to properly validate a select. And, we also needed to use a variable on our component, as an error flag. This is a technique that can be used for any control that requires advanced validation, not just the select control. Finally, we looked at validation at the form level, rather than at the control level. We saw how we could access properties on the ngForm instance to enable, or disable, the submit button. We enabled it once the form was valid. So, we've built forms, bound the data, and now, we know how to validate the data. In the next module, we'll see how to post this data to a server, and process any results we receive in return.
HTTP Form Posting and Data Access
Introduction
Hello, my name is Mark Zamoyta, and welcome to this module, titled "HTTP Form Posting and Data Access." We've looked at all the key aspects of forms so far in this course, so it's time to send our form data out to a server for storage. We'll use a traditional HTTP form post, and later in this module we'll also use an HTTP "get" to add data access to our form. We'll start off by creating a simple Node server, which will accept our post. This is just a test server, so that we can work with real HTTP calls, rather than creating a mock service, which uses mock HTTP calls. This server won't be saving any information, it will just handle requests and give short responses. If you're not interested in working with a Node server, you can also take a look at the MVC modules of my Pluralsight course, "Angular JS Forms Using Bootstrap and MVC 5." The MVC 5 modules of that course will show you how to set up an MVC server which will accept form posts. Once our server is up and running, we'll create an Angular 2 service to communicate with it. It's important to isolate our HTTP calls within this service so that our form component doesn't need to know any network details. The form just needs to work with an observable, which will shield the form from any HTTP implementation. Next we'll handle the form's submit event. We want to call a method on our component when the submit button or Enter key is pressed. We'll make sure the form is validated before we actually submit any data to the server. Once everything is okay and valid, we'll call our service to post the form data. We'll have to subscribe to an observable to handle any responses from our service. Next we'll look at handling errors returned by the server, we'll see how to work with observables to catch errors. Another common operation, usually performed after a form post, is to get any new ID generated by the server or data store. For example, when we create a new employee within a form, we'll definitely want to get whatever employee ID was generated on the server side. We'll see how to do this with our observable that's responsible for the form post. It's also very common to load data from a server dynamically, we'll look at loading the options for a select control dynamically, when the form is created. This requires an HTTP "get," and we can use the same service we used for our form post. So let's get started working with HTTP and Angular. We'll start by creating a test Node server in the next clip.
Creating a Test Node Server
Let's start off by creating a server that we can post to. I'm looking at package.json, and let's take a look at the server that runs when we run our Angular application. We call the start command right here, this is in the scripts section, and that calls npm run server, so server is the command right above it, and here's the web server that we're using. We're using webpack-dev-server, so webpack has its own server built in, and that's how it's able to get the live refresh and keep everything in memory for a fast development process. And it would be nice if we were able to post a form to this server, but there's just too much configuration involved, and it clutters up the whole experience of creating an Angular application, so it's actually easier to just go ahead and create a new Node server, and we'll do that. So we'll create a new project, and we'll create a simple Node server in order to post the form to it, and retrieve responses and error messages. I'll create a new directory called node_server, and to set up the project, I'll just call npm init, and we can just go ahead and select all of the default values for this. And there's just one package that we're going to need, Node already comes with most of what we need, but let's install npm install --save, and we'll install a package called formidable, and this'll help us process forms, it'll make it really easy. All right, so I'll start up Visual Studio Code from this folder, and let's take a look at package.json, and we see our formidable package right here, and that's all we really need. So I'll create a new file, and we'll just call it server.js, and this will be the server that we use to accept form posts. So I'm going to go ahead and paste in the code for the server. So here's our server.js file, let's walk through the code. It's a simple server that accepts posting of a form. We'll require http, and that's the built in HTTP server in Node, we don't need to use Express, and we'll require formidable, that's the package we just added, and we'll require util. So to create a server, we call http.createServer, and we passed it a function that'll take the requests, and the response. Now we need to set up core's access. We're going to be working, we'll see this in a second, but we're going to be working on port 3100, but the server that runs our Angular application is on port 3000, so just because there's a port change, it does mean that we need to set up cores, and cores allows us to do cross-origin requests. So that's what these two setHeader commands are for, with these headers in place, we're able to work across port boundaries, or different origin boundaries. So if the request method is post, we want to call this function processform, and if we're not posting a form, we'll just end off the results. So the key processing is in this function, processform, right below it. So we call new formidable.IncomingForm, and this does all the legwork for us to parse the incoming form and store it in the form variable. We'll call form.parse, passing it the request and a function. The function accepts an error, and fields. These will be the fields that we're posting for our form. We're not going to be doing any validation here, we just want to show that the form is posting properly to the server, so we'll write a successful message, 200, and then we'll paste all of the fields. We'll call util.inspect, which will format this fields property which is being passed into parse, and then below it we'll just write out to the console, the same exact thing. This way when we post fields, we can see exactly what's coming through on the server, and that's important to know on the Angular side. We want to make sure we're not missing anything. And that's all we need to do to set up form handling. We'll set up a port variable, and we'll listen on that port with the server, and we'll just log out a message, "server listening on port 3100." So I'll make sure this is saved, we'll go back to the command prompt, and let's just run it. We'll call node server.js, and the server is now listening on port 3100. So in the next video, we'll go back to the Angular side of things, and we'll leave this server running. Like as we post to the server, whatever we post will show up here on the console, so this is a good debug tool.
Creating an Angular Service to Post a Form
So our Node server is all setup, waiting for a form to be posted, so how do we post the form to the server? Well the first thing we're going to want to do is to create a service, that way we can remove all the HTTP logic from the entire application, and keep it isolated in the form posting service. So let's create a new folder, I'll go to app, and we'll call the new folder services, and I'll create a file there called form-poster.service.ts. So let's put together a class for our form poster service. We're going to need to decorate it with the Injectable attribute, and this will make sure it gets injected properly within Angular. We'll export class FormPoster, and we're going to need to import Injectable. We'll get it from angular/core, and while we're up here let's also import Http, and that comes from @angular/http. This is Angular's object that lets us work with the HTTP protocol, and we could actually inject it into this form poster class. I'll create a constructor, and if you're not too familiar with TypeScript, this is a shortcut here. Instead of declaring a private variable above the constructor like you would usually do, you can just go ahead and declare the private variable within the constructor. So lower-case http is our private variable that'll let us work with the HTTP protocol, and we won't get this wired up just yet, let's just create a method called postEmployeeForm, and we'll pass it an employee, and we'll import that as well, that's in our models folder. We'll go up one folder, and into models/employee.model, and we'll just log it out. We'll get this working later in this module. So there's the starting point for our service to post the form. So we've labeled FormPoster as injectable, so let's inject this into our home.ts file. We have our home class, and we'll create a constructor here, and we have to import FormPoster, and we'll get it up one folder in services/form-poster.service. Now just because we're injecting it here, we're still going to get an error because we forgot a step. Let's just see what error we get when we try to run this. Make sure everything's saved, the form refreshes, I'll bring up the debug tools, and let's take a look at the error we got. "No provider for FormPoster." So we can't just go ahead and inject a service like that, we have to mark it as a provider in Angular. We could add it as a provider here in the component metadata, but we may want to provide a single instance for the entire module. We can do that by adding the service provider in our NgModule, so let's go to app.module.ts and it has a provider's item here in our NgModule attribute. So I'll list it as the first one, it's called FormPoster, and of course we need to import that. That's in the current folder services/form-poster.service. So I'll save this and our form will refresh, and you can see it's clean now, we're not getting the error. So looking back at home.ts, everything's setup, we have our FormPoster, that's going to be injected when this runs, so now we're just going to have to make use of that on a submit, and we'll take a look at handling the submit button in the next video.
Handling a Form's Submit Event
We've been doing a lot of setup, and we haven't actually done anything to submit the form, so let's get started with that. First of all, let's take a look at home.html, this is our template for the home component, and in order to submit it we can specify the submit event, and again, events are in parentheses, and we'll set that to the function we want to call. We'll just call it submitForm. Earlier in the course we setup our #form template reference variable, and that's being set to the string ngForm, which represents the ngForm directive that Angular automatically places on every form. So let's go ahead and send that form to our submitForm function, and we don't need the hash in front of it, we just type form. So I'll save this, and we'll go to the component, home.ts. Let's create our method, submitForm, and it gets passed form which is type NgForm. That's the type of a directive that Angular places on every form, and let's import that. NgForm comes from @angular/forms. So now that we have access to the form when the submit button gets pressed, let's just take a look at our model when this occurs, and remember to use this.model, we're inside of method. And the forms blank, but I'll just hit the Ok button to submit it, and you can see that our model has all the default settings. firstName, LastName are blank, isFullTime is false, there's no paymentType, and the primaryLanguage is set to "default." Probably one of the most important fields on form is also its own model, which is different from this.model, but let's take a look at form.value. And value is the kind of, behind the scenes model used by the form, let's print that out. I'll save it, we'll get the auto-refresh, I'll hit the Ok button to submit the form, and you can see we get a similar object. So you don't necessarily need to use your own model in a component like this, you could go ahead and just let Angular control the model for you, internally, and that really depends upon your requirements, and how you have your data classes setup, but you can do it either way. But to make this more useful, we're going to want to send this to our FormPoster service, so we'll call this.formPoster, and let's take a look at what the name is again, of the post method. It's postEmployeeForm, we pass it the employee, and the employee is held in this.model. So I'll save this, we'll go back to the service, and we'll see that we're logging out the employee that's being passed. Let's make sure we've refreshed, I'll hit Ok and we get the employee. There's no validation being done yet, there's some validation being done on the form itself as we covered in the last module, but for something like Primary Language, which Angular doesn't know how to handle, we're going to need to make sure that that validation gets done in our submit method, so we'll take a look at that in the next video.
Validating the Form Before a Post
Before we submit the form, we want to check out our validation and make sure everything's good. So in our submitForm method right here, we can take whatever needed steps right before we post it. And before we do this, let's look at some of the validation that's already setup, or that we can quickly add. I'll go to home.html, our template, and let's just make firstName and lastName required, and I'll give it a similar error message. We just need to give this input a template reference variable, lastName="ngModel" and I'll clean up the error message. So our First Name field is required, our Last Name field is required, we're not validating a check box and we're not validating a payment type, could certainly do that. But we are validating the Primary Language, and we have a validation function for that, we just need to call it. So let's go back to home.ts, and we'll call this.validatePrimaryLanguage, and we have to pass it the primaryLanguage that was chosen, so we'll pass it this.model.primaryLanguage, and if you remember that sets this variable here, hasPrimaryLanguageError. So if we have that error, we'll just return. Now that error itself will cause the primaryLanguage and its select to be shown in red, so that should be okay. I'll save it, the form refreshes, and right now our Ok button is disabled. That's correct, because First and Last Name aren't entered, so I'll just enter a bogus First Name and Last Name, I'll select some things, and we'll leave Primary Language blank, so this should cause an error when we hit Ok. And that worked fine, it's highlighted in red. If we select a language and hit Ok, we can see in the console that the call to our service did occur, and all the values are set correctly. firstName, lastName, paymentType, primaryLanguage, it's all good. So if you had a complex form, you could probably break out all of your validation logic into its own function, but it really depends upon the complexity, and how you want to go about doing it. But for now this is good, in the next video, we'll take a look at observables, and actually using the observable when posting our model to the server.
Setting up an Observable and a Subscriber
We're ready to post our employee form to the server, so let's look at our submit for method again, right here. We have a validation section, and then we use our FormPoster service to call postEmployeeForm, passing it the model. In Angular 1 we would work with promises, but now we're working with observables in Angular 2, so postEmployeeForm is going to return an observable. And what we can do with an observable is subscribe to it, and when we call subscribe, there are two methods we need to set as arguments. The first one will return data if it's successful, and let's just log out that data, and the second argument to subscribe will be the error handler, we'll log out the error. Now we're getting the red squiggly under subscribe because VS Code isn't aware yet that our service is going to return an observable. But this is good enough for now. If the posting is successful, we'll show success in the data and if the posting has any kind of error attached to it, we'll print out error with the error object. So let's go to our service, I'll save this first, and there's a lot we're going to need to do in the service in order to interact with HTTP. First of all, we want to keep our HTTP code confined to the service, we don't want it to leak out into our home.ts file, that's our component. So the interface is really this observable here that gets returned from postEmployeeForm. So let's make sure that it does return an observable, and I'll make it an observable of any, and there are a few imports we're going to need, I'll paste those in. So we need Http from angular/http, and also we're going to need Response, Headers and RequestOptions from angular/http, I can put these together. We're also going to need to import rxjs/Rx, and those are our reactive extensions for JavaScript. That's what lets us use observables and manipulate data with them. We'll also import Observable from rxjs/Observable, and all of this is setup when we installed the Angular 2 seed project. Let's just take a quick look at that. If we look at app.module.ts, you can see in our imports that we do have HttpModule, and that's what gives us access to HTTP. Just remember it needs to be here. And also, if we look at our package.json, we can scroll down here and we see @angular/http loaded right here, and the rxjs package right here, and all of that was setup for us with the Angular 2 seed project. So going back to our service, I'm going to go ahead and fill in the body a little bit on this method right here. So we'll create local variables for the body, and this is a JSON.stringified version of our passed employee, that's what we're going to post in the body. Then for our headers, we use the HTTP Headers object, and we'll set the Content-Type to application/json, because JSON will be in the body, and then we'll set up our request options on this line here, making sure the headers get set. So the next thing we do is we return an observable that we create. Now we're going to need an HTTP object, and that's going to get injected by Angular. So let's go to our constructor. If you're coding along, you may have already injected the HTTP instance, but we'll need to add it for this project, and we'll create a private http of type Http with a capital H. So now we have a private property, and that's fine. And we're going to post to the server that we created earlier in this module. That was located at localhost, but it was a different port, it's at port 3100, and in that server, if you remember making that, we're just grabbing any post, so the actual endpoint doesn't really matter at this point, we just created a quick server that we can use for testing, and we're going to pass it the body and options. So on our observable, we'll then call.map, and this is where we will extract the data that comes in from the server, and at any point if there's an error, our catch will get called. It'll call a method called handleError, so let me add these two methods, extractData and handleError. So let's look at extractData first. It'll be passed the Response object, and what we need to do is call Response, res.json, and that'll get us the body from the response. We might need to use that for a few different reasons, and we'll see more about that later in this module. But once we have the body, the field we're interested in is fields, because that's what our server uses to put all of the form's posted fields, and if for some reason that's null, we'll just return an empty object. So extractData, that gets called from our map call right here, and then on the catch call for the observable, if any error gets thrown, we'll call handleError right here. We'll just log out an error message, post error, because there was an error posting the form, and we'll show the error. And we want to stay in the context of an observable, so we'll return Observable.throw, and we'll just use error.statusText, we'll throw that string. So we're all setup to work with HTTP, and just remember that we're returning this observable here, and an observable isn't going to execute unless there's a subscriber, and we did setup the subscriber already. Looking back at home.ts, we subscribe right here. So in addition to those methods being called within the service, we'll also get these two methods called right here, our success and error methods. So let's make sure our server is running, and see if we can get this to work. We'll do that in the next video.
Posting to the Server
So we have everything all setup, and our Node server is running. You can see I executed node server.js, this is the server that we created early on in this module, and it is listening on port 3100. So we have our form, let's refresh this, and let's fill it out and submit it. I'll enter some dummy data, and we need to make sure it validates, so I'll select a Language, and I'll press Ok to submit it. So the first thing we notice is that we are getting a JSON object returned. This is the employee that was posted, it's being returned from the posting now, and the values look good. But we got an error if we look in the console here, let's look in more detail. So we got "post error, SyntaxError, "Unexpected token f in JSON at position 2." So if we look at our returned JSON, the posted fields up here, position 0 would be the curly brace, position 1 the space, and position 2 would be the "f" in fields, so there's some error with that, and it appears that when we call JSON.parse, it's looking for the stringified version, it wants to see fields in a string. We could parse this using eval, I mean it looks like a valid object literal, but we want to avoid using eval whenever possible, there's just too many security concerns with it. So let's have our server return a stringified version of this object. Here's our server.js file, and instead of returning util.inspect fields, let's just call JSON.stringify. Let's just create a local variable, because we're going to need this twice. So we have a data variable, and we'll call result.end, passing that data, and we'll also log it out to the console here. So I'll save this, let's go back to our console, and I'll shut down the server and restart it. Actually let me clear the screen first. We'll go back to the form, let's just clear this out, I'll refresh it, we'll enter a First Name, Last Name, select a Language, and we'll post it with the Ok button. And everything seems fine now, we're not getting an error message, and our success handler got called. We're showing success along with the object that was posted to the server. If we look at our server log here, we can see that we do have a stringified version now. We do need to have our name value pairs, well everything needs to be in quotes, so "fields" here is in quotes. So when you're working on HTTP, you really need to be aware of the data that's going back and forth. Small problems in the formatting of a JSON object can cause problems, like we've seen. Another problem you might run into is, we're using the variable "fields" here, but many servers also like to use the term "data" there, so let's look at the code quickly here. Instead of using "fields" they would put "data" as the key, and if you were to do that, you'd also have to take a look back at the code that parses the JSON, which is up here in extractData, and instead of body.fields, you would look at body.data. But this is good, and everything's working for now. Our http.post worked fine, the map worked, and because of that our subscriber got a successful message. Looking at home.ts, we got the output "success:" and it showed the data that was returned from the server. To look at that one more time it's right here on the bottom. So in the next video, we'll take a look at handling an error that comes back from the server.
Handling a Server Error
I'm looking at our server.js file, and this is just a quick and dirty server that we put together in order to test forms, and we can see here that we're calling response.writeHead, going to write the header, we're giving it a status code of 200, which is successful. So let's change it to something much worse, like 400 which is an error. Anything in the 400 range lets the browser know that something awful happened. So I'll save this, and let me stop the server and restart it, and let's try to post the form again. Make sure it's valid, and I'll post it by hitting Ok, and we got the errors we expected. I'll make some room here. So we got our post to the URL, and we got error 400 which is good, we expected that. Also our subscriber captured the error too, we're printing out "post error:" and we're printing out the entire error object. And here we could grab the status 400, or we could look at the statusText, "Bad Request" and we can also still post a body like we did. So let's just review the code again, and make sure that these are the messages we expected. We can see in our service call to postEmployeeForm, that's our subscriber, we get an "error:" along with the error, and that's what we got right here, "error: Bad Request," and looking back at our actual service, we would catch anything in this.handleError right here. So here we have "post error," and that's what's printing right here, along with our error object. So this is where we would handle any logging within our application, we would certainly want to write something out to an error log, and also make sure we throw something that would be valuable to the user. Right now we're just sending the error.statusText as a string to be thrown, but you could go ahead and throw the entire error if you wanted to. So everything's working fine, we tested this in a successful way, and we tested it with an error result. There's one other valuable thing we can do in posting a form, and that's getting some kind of ID back from the posting. For example, if you wanted to add a new employee with a form we're going to want to know what ID the server used to save off that employee in the data store, so we'll take a look at that in the next video.
Retrieving an ID From the Server
Let's take a look at returning extra information from the server after a posting. Here we can see what's getting returned, essentially it's the exact same employee object that we posted, but if the server makes any modifications, well why not include that information as well? The usual example is, you're going to need an ID. Your employee's going to be saved off to some data store with a unique ID, and we need to know what that is, so all we have to do is add it to this fields object, and that stands true for any other type of information we might want to send back from the server. So let me shut down the server here, and let's take a look at server.js again. First of all, let's change our error back from 400 to 200, successful, and every server is going to generate an ID in a different way. And since this is just a test server, let's go ahead and take our fields object and add an id to it. So just make up an arbitrary ID number, and remember that our form.parse call, that's what sets up fields for us. So I'll save this, let's restart the server, and let's post our form again. We still have our error message from the last attempted post in the last video, so our form's still filled out. I'll hit Ok again, and now we get "success." If we look at the returned data, we can see word wraps on here, but we get, starting right here at the end we get "id":"ABC123." So we are getting our ID back. In order to process that, we can look at the log, and we can see this is the object which also has ID set within it. Let's open it up, and we get id: "ABC123." To access that in our component, let's go to home.ts. We could just turn this into an actual function call, or we could expand it out with curly braces, but our data here is what will contain the new ID. So depending upon how you want to process that on the client side, that's up to you, but you can grab the ID out of this object.
Loading Data from a Server
So we've seen how to post form data, but there are other reasons why we might want to access the server. Here's our Employee Form we built earlier in the course, and you can see the Primary Language field. It says Select, and the options right now are hard-coded, so what we're going to do in this and the next video is really to load these options from a server, and we're going to need observables, and setting things up is going to be a lot like what we had to do for working with a post, to post data to the server, so let's see how to do this. Here's our server project that we built earlier, and again this is the server that's running on port 3100. If we look at our main function, which is http.createServer, this is our function which handles requests and returns responses, and this part in particular is where we handle posts, so let's handle a get. We'll use the get verb to retrieve the list of languages we want. And because this is a test server, we'll just handle any "get," we just want to make sure this functionality works on the Angular side. I'll paste in the body of this "if" statement. So let's take a look, so if the method is "get," we're going to create an object literal called "data," and that'll have a data item, and that's an object that will have a language inside him, and that's where we'll list out our language strings. And again, this is all stored in a local variable, data. Then we'll take our data and we'll call JSON.stringify on it, we'll store that in responseData, and then it will call res for response, .end, passing it the response data. So this is how the data gets back to our application when we call a "get" method on the server, and we'll just log out a "get" and the response data to the console for debugging. And again, any "get" will be handled this way, so this is only for testing this one feature. So I'll save this, and let's run the server. I'll call node server.js, and we're listening. So let's go to our Angular project now, and here's our component, I don't want to work with this just yet, I want to work with the service, so let's go go form-poster.service.ts. So this is our service for posting forms, and here is the method we called earlier, postEmployeeForm. We could treat this more as a general service for handling anything related to the form, so let's just do that. I'll add a method called getLanguages, and that's how we'll retrieve the languages from the server. So here's the method, let's take a look at it. It's very similar to postEmployeeForm below, we return this.http.get. For the post we returned a post call, but now we're going to call "get" in order to return the observable. You can see above that we're going to return an Observable of any, and we'll go to localhost, port 3100, and the endpoint will be get-languages, but that could be anything. Right now our server is setup to return languages for any "get", so when we get our results we'll call this.extractLanguages, we have to create that, and if we have an error, we can use the same error handler that we used for the post, that's fine. So let's create the extractLanguages method. It'll go under extractData. So here is extractLanguages, again it's very similar to the way we extracted data from our post. We'll create a body local variable, and we'll call response.json, that'll get the response data from the server, and it'll return body.data. All of our data is stored in that data object, if that's undefined or null for some reason, we'll return an empty object. So this is straightforward, and our observable is all setup. When we call getLanguages we'll return this.http.get, and we'll map those results, and on any error we'll catch the error and call the error handler. So I'll save this, and one thing to keep in mind when we're working with observables, if we called getLanguages right now, it's not necessarily going to go out to the HTTP server. An observable needs to be subscribed to, like we saw with the post. In the next video we'll go to our form component, and we'll actually subscribe to this. We'll call it, get our observable, and we'll subscribe to that observable.
Dynamic Options for a Select Control
So in the last video we set up our server, and we set up a service to access the server. Now let's look at home.ts, that's our component that contains our form, here we can see that we're hard-coding languages, so we need to call getLanguages on our service, and we can do that in the constructor. We'll do it right away, right when our form starts up, and we can follow the same pattern that we did when we posted the form, right here. We called this.FormPoster.postEmployeeForm. So I'll add in the code to getLanguages. We're calling this.formPoster.getLanguages, and we need to subscribe to it, that's what makes the observable actually reach out to the server, and grab the data. When we get our data, we'll set this.languages equal to data.languages, the data is our input right here, and on an error we'll handle it the same way we did with a form posting, we'll just log out "get error," and we'll show the error object. Now just to make sure this works, let's just wipe out our hard-coded languages, and I'll save this, we'll get the refresh. So our form refreshed, we'll open up our Select, and we can see that we do have the list of languages. German was one of the languages that came from the server, that wasn't hard-coded before, so we know it is coming from the server. If we look at our server log you can see that get was called, there's an object with a data field, and that's an object with languages set to an array of languages. So our server's working fine, and everything went through to the Angular application. I didn't select a language so we're getting a validation error, but when I select a language we're good to go. One problem that occurs frequently with data access, and you have to account for it, is some kind of a delay in the data access, and we can simulate a delay by using observables. Let's go to our FormPoster service, and after we call our server, we can call delay on the observable, and let's just delay for five seconds, 5,000 milliseconds. So I'll save this, we'll get the refresh, and let's look at our drop-down. The languages haven't loaded yet, we have to wait five seconds, so I'll click on it again, and now our data has loaded. So that could possibly cause a problem for our users. If there's a longer delay and they try to select a language, nothing will come up, and it'd be very confusing. So how do we deal with this? Well there are a few ways, but one way that comes to mind first, and the easiest way, would be to hide the form, so let's go to our form, our template, home.html, and we can actually hide this entire div that contains our form. We'll place an ngif on it, *ngIF, capital I-F, and we only want to show this div if languages is populated. So if languages.length is greater than 0, we'll show the employee form, so I'll save this, our form's refreshing and it's not showing, and here it is after the five second delay, and we select a language, and everything's good. One other thing we could do is display a loading tag, that way the user's not left in mystery. And I'll just copy the same ngIF structural directive. We'll just test length against 0, so I'll save this and we'll get our refresh, we get our loading text, and the form shows after five seconds. Our languages are good. There is another way to handle this, rather than shutting off the whole form. One other technique you could use to load these languages is called an async pipe, and I'm not going to get into that with this course, but there is a course that goes over it very well. If you look at "Angular 2: First Look" by John Papa, you can see a module called "Data with Http," and there are two specific videos with async pipe, right here. The first one explains async pipe, and the second one gets it working with an observable. And that's just an alternate way to do what we're doing. I recently found out this course will be updated soon, so you may have to search for async pipe if it's no longer located here. What we did in this video was we simply called the observable in the constructor, and that's a valid way to go as well as with async pipe. So we've seen how to access the server with a post, posting the form, and we've also seen how to access the server with a "get," to get this list of languages. And that's all we're going to cover with HTTP, so let's wrap this module up with a summary.
Summary
In this module we created a test Node server to accept a form post, we created an Angular service to handle all server access using HTTP. We used the separation of concerns principle to keep our network communication logic in this service. Next we handled our form's submit event, and made sure all form data was valid before continuing. We used Angular's HTTP module to post our form data, and we worked with an observable to handle any success or error codes. We saw how errors flowed through our observables, and could be detected by our form. We also saw how we could retrieve an ID for a newly created employee from the server, after a form post. Finally, we loaded some data dynamically from our server as a form was created, we retrieved a select controls options this way, making our form more flexible by allowing us to remove hard-coded values. So far in this course we've covered everything from form creating to form posting, and you're able to create some useful forms for your applications. The next and final module of this course will cover some very useful third-party controls, that many forms will need. We'll see how our forms can make use of dates, times, toggle switches, a more modern style of radio buttons and a rating control, so I hope to see you in the next module.
Third-party Form Controls
Introduction
Welcome to this module titled Third Party Form Controls. My name is Mark Zamoyta and in this module we'll be exploring a rich set of controls we can use in our Angular 2 forms. We'll be specifically looking at the ng2-bootstrap controls. However, many of the techniques and patterns we use in this module will be valid for other control libraries as well. And speaking of other control libraries, I want to show you a valuable resource where you can look for Angular 2 form controls. Here's Angular 2's website, angular.io. If you scroll to the bottom, you can see this Tools & Libraries link. Follow the link and there are many resources that will help you out with Angular 2 development. I'll focus on the UI Components section because that's where you'll find controls most useful within forms. Here's ng2-bootstrap which this module is all about. You don't want to get it confused with ng-bootstrap which is in alpha release and is more focused on Bootstrap 4 which is also in alpha release. Ng2-bootstrap is a free, open-source library. If you look around this list, you'll find other free and also paid libraries that will contain form controls. I wanted to focus on ng2-bootstrap because it's in very active development and the components have worked well for me. In this course module, we'll start off by installing ng2-bootstrap and integrating it into our form application. The first control we'll look at is Datepicker, which lets us select a day within a calendar-based user interface. We'll see how to work with several properties, and most importantly we'll see some of the problems you'll run into using third-party components. You'll have a great understanding of how third-party components fit into Angular 2 apps and you'll be able to deal with some common problems when using form controls from any vendor. Next we'll look at a Timepicker control. This works with a simple user interface and works on a JavaScript Date object similar to the Datepicker control. The third component we'll look at is a toggle button. These are especially popular on mobile devices, but we see them more and more on desktop apps too. Next we'll look at Bootstrap-styled radio buttons. These acts like the traditional radio buttons we saw earlier in this course but they're essentially laid out in a row of toggle buttons. Finally, we'll look at a rating control. Traditionally, stars are used as icons in rating controls, but since we're working with Bootstrap 3, we have access to glyph icons. We can use any image from that set for selected or un-selected rating symbols. So let's get started adding these controls to a form. First we'll need to install ng2-bootstrap with npm, which we'll do in the next video.
Setting up ng2-bootstrap
I'm here at the GitHub repository for ng2-bootstrap. The repository is valor-software/ng2-bootstrap. So here are the files for the open-source project, but let's go ahead and click on this link to take us to their website. So we have Native Angular 2 Directives for Bootstrap, and we'll be working with Bootstrap version three, but it does say that it works with version four as well. If we look on the left side of this page, we can see all the different directives and components we can use in our apps. Some of these directives aren't meant for forms specifically such as an accordion or alerts, but we'll definitely be looking at things that can be used in forms, especially the datepicker and along with the datepicker, I'll scroll down a bit, we can see the timepicker, and we'll also be looking at buttons. We can use these for toggles or radio buttons, and there's also a rating control, and we'll cover these throughout this module. But for now, let's get set up. If we scroll down we can see the installation instructions. We need to npm install ng2-bootstrap with the --save flag. So let's do that. Npm install. Ng2-bootstrap with --save. That'll put it in the package.json file. You can see we get some peer dependency problems, but let's come back to that. Let's look at the warnings first. In these warnings we have ng2-bootstrap 1.1.8 requires a peer of @angular, common and it's looking for version 2.0.1 and we're working with Angular 2.0.0. So that's fine. The last number in this server version is just for minor bug fixes. There aren't any breaking changes when this last digit changes in the version number. So we should be fine with these warnings. And looking at our peer dependencies up here, these aren't really errors. They're written in red, but you can see that its listing not common@2.0.1, but it's specifying that it will use common@2.0.0 and that's fine. Most likely when you see this, all these versions will be updated. Ng2-bootstrap updates regularly. I think just yesterday it was 1.1.6, and the versions of Angular change very quickly. As I've been developing this course just in the last month it's moved from release candidates to 2.0.0 to 2.0.1, and there might be another bug fix coming soon. But the important thing to know is that we're fine. We're okay to use the 2.0.0 version of Angular. So let's take a look at the project. If we look at home.html, our template, I cleaned it out. We're going to start fresh when we work with these ng2-bootstrap components, so let's just make sure this runs. We'll kick off the server with a npm start. And everything looks fine. We have Angular 2 Forms showing up as a header, and there's nothing in the form. And just to make sure we have our package for ng2-bootstrap, let's look at package.json and we see it listed right here. Ng2-bootstrap version 1.1.8, so we're good to go. In the next video we'll take a look at using the datepicker component.
Datepicker
Let's start off by working with the datepicker. I'll click on datepicker on the menu on the left. And here we can see it in action, it's a full month calendar and I'll just scroll down and we'll take a look at how to use this. So looking at the Usage section, we can see what we need to import. We have a DatepickerModule and it's located in ng2-bootstrap/ng2-bootstrap. So we'll just grab this line. And I'll copy it. In our project let's go to app.module.ts, and here's where we can import it. So we have our DatepickerModule, and we need to put it in the import section so that we can have access to it. So that's all we need to do to get set up. Just remember as we work with different components in ng2-bootstrap, we need to list them in the imports of our NgModule. So I'll save this and let's go back and look at the home.html page. Here's our template, and let's just add a datepicker to our form. Before we do so, if we wanted to look at the website again, we can see it in action right here. I'll zoom in a bit. So the tag is datepicker, and we'll just start off by creating one that's relatively empty. We're going to need the ngModel but we don't need to look at minDate or showWeeks yet. So we'll specify datepicker and we'll give it a name. Let's call it startDate and we'll bind to ngModel with the banana-in-a-box syntax. And let's create a variable on our component called startDate as well. Earlier in this course we would bind to model.startDate, but in this module we're mainly interested in working with these controls. So just bind to a property directly on a component. So I'll save this and we'll get the refresh. And we have our calendar, and let's look at it a bit. We can use the arrow keys to go from month to month, so August 2016 to September to October. When it started, the fifth was selected, or highlighted. Let me refresh to get that, that's today, the fifth, when I'm recording this. And we can also click on the month and year. When we do that we get a list of all the months, and then we can scroll through the years. If we click the year again, we get much bigger set of years. And I'll just drill back down into today's month and day, the fifth. So we have this daily view and we also saw the monthly view and yearly view, so what about selecting a date? We know that we're going to bind to startDate, so let's just take a look at that. I'll display that underneath the form. And let's make sure we add that to our component. And we'll give it a type of Date. Notice that it'll start off uninitialized, so it'll be undefined at the beginning. I'll save everything and we get the refresh, and it's not showing a date because it's unrefined right now but let's click on the 12th. And that's the date that shows up. Wednesday October 12th 2016 and a time of midnight. And that's just the default way that JavaScript in Chrome displays a date, but we are working with a Date object here. And as we select different dates, that will change, of course. Now we're on Thursday. So one problem that I see with this is if we expand the window, we can see that the border around our calendar takes up the full width of the screen. So that seems to have a block display but we could change it to inline-block, let's do that. I'll go to the template, and one thing we can do also is wrap our datepicker in a div with Bootstrap styling. We'll give it a class of form-group, and we'll also give it a style. We'll set the display to inline-block. So I'll refresh it. And now we're fine. So let's take a look at the documentation and see what else we can do with a datepicker. If we look at the datepicker properties, you can see we have an initDate. That'll be the default date to show if there's no ngModel value. So we can either use that property or we can just go ahead and set an initial value. Let's initialize this. We'll set startDate equal to new Date, and let's just set January 1st, 2017. And I'll save this, we'll get the refresh. And now you can see that the date is set already, on start-up, January 17, the first. So to look at using properties in the html template, one thing we can change is this week number right here. You can see the list of week numbers throughout the year, and we can remove those easily. If we look at the properties, there's a property called showWeeks and we can use that. It defaults to true and that's what we've seen already, and let's just set it to false. We'll set showWeeks equal to false. Now it's very easy to just type showWeeks like this, but remember, we're binding to a property and Angular 2 has this unique way of binding to properties, and we need to use square brackets. I used to make this mistake a lot and I still do once in a while, but we definitely need these square brackets. I'll save it. And now our weeks are gone on the left-hand side. And what would happen if we just happened to forget those square brackets? I'll refresh. You can see nothing happens. We don't get any kind of error message, the weeks are still displaying, and a lot of developers have spent a lot of time scratching their heads saying "what's wrong?" So just always be sure when you're binding to properties to use the property-binding syntax which is putting the attribute in square brackets. And everything's fine again. Looking back at the properties, we have this property called datepickerMode and it defaults to day. But we can also default to a month or a year, and we just saw that earlier. There's a month view and a year view. So let's change this, datepickerMode is the property. Make sure we do our property-binding syntax. datepickerMode equals, let's say month, we'll pick a month. Now this is actually going to cause a problem, and I'll explain it in a second, but we need to wrap month in quotation marks because it's a string. I'll save it and refresh, and nothing happened. It's probably because we have a date selected already. So let's un-select that in our component. We'll just specify that startDate is a Date. So now we're able to select by the month. If we drill into, let's say February, we can go ahead and pick a day in February. So that's fine. We can also set the datepickerMode to year, and now we get a list of years, you can drill into a year, a month, and pick a day. So I want to go over this syntax right here. Why do we need two sets of quotes? Well obviously the first set of quotes, the double-quotes, that's because we're working with an attribute for a datepicker, but why does year need to be in quotes? Angular has a built-in compiler and it's going to try to compile this as an expression. So to put a string in an expression, it needs to be quoted as well. So that's why I've put it in single-quotes. If we left out the quotes, then it would look for a variable called year directly on the component and we don't have that and we don't want that. That wouldn't work. So just make certain that when you're binding to a string literal, it needs to be quoted, otherwise the Angular compiler is going to look for something else. It'll look for some way to resolve the variable with the component. And let's just see what else we have for properties. We have a minDate that we can set as a Date right here. Let's do that. I'll just overwrite this datepickerMode, we don't need that. We'll set minDate equal to, let's set it to minDate and that'll be a variable on our component. I'll save this and go to our component. minDate equals new Date and let's just say, it's October fifth, let's just put it a week into the future, October 12th 2016. I'll save it and we can see that the 12th is selectable, but before that it's not selectable, so that's working fine. Everything's grayed-out before the 12th, which is our minDate. The key thing I wanted to point out, though, is in our template. minDate is obviously a variable coming from our component, but if we tried to enter a date in here? Instead of minDate, what if we said the same thing here? October 12th 2016. I'll save it and we get an error. Unexpected token 12. So remember, the Angular compiler is going to get a hold of this and try to parse it. It has no idea what this means. And what if we created a string? I'll wrap it in single-quotes. I'll refresh and we get another error. date2.getFullYear is not a function. So minDate is obviously looking for a date, but it's getting a string. So that's no good, that's something to watch out for. You can't just substitute a string for a date. So are we able to new up a date here? New Date, and I'll save that. So we'll get the refresh and again we get an error message. Unexpected token Date. So the Angular compiler just isn't aware of what this is and it's unable to compile it into something runable within Angular. So in a case like this, our best option is just to use a variable on the component, minDate, which is what we did in the first place and that worked fine. So there are several other properties that could be useful, especially at the end there's a bunch of formatting-type properties. But that's all we're going to look at now for this video. In the next video we'll take a look at the timepicker.
Timepicker
Next we're going to look at the timepicker. If we select the menu, we can scroll down and click on the timepicker item, and here's the component right here. We have up and down arrows to set the hour, minutes, and a toggle for AM and PM. We could also type in a value, let's say we had 05 minutes. And we can hit the spacebar to toggle between AM and PM. So let's scroll down and see how we can use this. Again we import TimepickerModule from ng2-bootstrap-bootstrap/ng2-bootstrap, so I'll just copy this line. We'll go to our app.module.ts, paste in the import. And again, make sure we copy this and paste it in our list of imports right here for NgModule. And I'll save it. We can go to our form, home.html, and I'm just going to go ahead and copy this and then comment it out. When we start building a long form, there's just not enough screen real-estate, so I'll just create a simple form that only has this one time control in it. So it's called timepicker. We don't need the style any more. And instead of startDate, we'll use startTime. We haven't set that up on our component yet but let's save this and see what we get. Well, we have an error. We have to copy timepicker to the closing tag. And there's our time, it's 11:49AM. We can toggle AM and PM. We can scroll up and down for the hours. You can see it switches from 12 to 1, and it goes from AM to PM. Or no, it actually stays on PM for 12:50. There we go, 11:50 is AM and when you scroll up to 12:50, it's PM. So that's our control. So let's initialize it. We'll set the startTime variable on our component. We'll specify startTime and that actually uses a Date object and it really doesn't matter what the date is, but it does matter what the time is. Let's set it to, let's say three PM. So I'll save it and our time is set to three PM. So just notice that it ignores the date part and it's only concerned with the actually time within the Date field. And it seemed to default to the current time, so let's just leave this blank, we'll just specify to typescript that it's a date. And it's 11:51, let's see what gets set. 11:51, so that's good. It defaults to the current time. Let's look at some of the properties on the timepicker. I'll scroll down. It has some styling for tabs or pills up here. We're not going to cover those. You can see these properties hourStep and minuteStep. Let's say we wanted to change the minuteStep, we want to work in, let's say 15-minute intervals. So let's set that. We can set minuteStep and remember to put the property-binding, square brackets. And we'll just say 15 for 15-minute intervals. Now when I step up in minutes, you can see that it jumps up by 15 each time. So in a case like that, you'd probably want to zero out your minutes, let's just do that quickly. We'll go back to setting it to three PM again. So three o'clock, 3:15, 3:30, 3:45, and our stepper is working well. You can change the meridians, the AM and PM strings. You can set it from 12-hour mode like it's set now to a 24-hour mode and you can also set a min and max time that the user can select. So we won't go into the rest of these properties, but just remember the gotchas that we saw when we worked with the datepicker. When you specify a date, you should be binding to a variable on the component. You can't really set a string or new up a date within the template. Also when you bind to a string, make sure you put the quotes in there as well. So that's that the timepicker. In the next video we'll take a look at a toggle switch.
A Toggle Switch
Let's look at ng2-bootstrap's toggle switch. I'm already here at the button section. You can just click on the menu and select buttons, and the first example here is a single toggle and that's what we'll be looking at in this video. You can see when I click it, the value changes from one to zero. And also when it's one, it looks a little bit selected with a darker color on the background. If we scroll down a little bit more you can see radio buttons and we'll cover those in the next video. But for now, let's just see how we can include this in our project. We'll grab the import statement, we have to import ButtonsModule from the same place as always, and we'll just add that to our NgModule. I'll add the import, I'll grab the ButtonsModule text and just paste it in, so we have ButtonsModule set up. Going back, we can see how we use this. We specify a button and it has ngModel, and it also has some directives, btnCheckbox and a btnCheckboxTrue, that will be the true value. We could set that to any string. Or btnCheckboxFalse, that's a zero value. So let's just copy this and use this as an example. So I pasted that button here, let's fix it up. Let's set the ngModel to, let's just call it onOffSwitch. And we have our btnCheckbox directive as we saw in the sample and for now let's just leave true to one and false to zero. Let's just make sure it works. So we have our toggle button. And we do have an error, let's see what that is. Oh, if ngModel is used within a form tag, we need to set the name attribute. So that's an important thing to point out. When we look at these samples here, these samples don't exist in the context of a form. There's no form tag, so it doesn't need the name attribute, but when we work, we are working in the context of a form. We have our form tag right here, so in that case we do need a name field. Let's set name equal to onOffSwitch and I'll save it. And now we're fine, we don't get any error messages, and we do get the dark and light highlighting, depending if it's set or not. So let's take a look at what this variable onOffSwitch is being set to. First off let's add it to our component. I'll just set it to any so we can see what it is, and we'll show it. So I hit the toggle and it's set to one. I'll hit it again and it's set to zero. So it toggles between one and zero. What happens if we change true to, let's say on, and I'll set false to off. Then we get the strings on and off. So we're not limited to ones and zeros. We can set any string we'd like. So also on a toggle like this, it might be helpful to change the label. So instead of saying Single Toggle, we can go ahead and change this to a binding, and we'll bind to onOffSwitch. And just to make it look better, let's just capitalize on and off. And we're getting this small box because there is no label. So let's initialize it to off. I'll go to home.ts, and the label starts as off, I click it and it turns on. So our label is changing fine. Now one other problem I have with this is that the shades of blue that are being used by the Bootstrap template, they're pretty close to each other. They're really hard to differentiate. So let's go ahead and bind a class to this onOffSwitch as well, that way the appearance will be more dramatic whether the button's on or off. So we're setting classes of btn and btn-primary, but let's just go ahead and take out btn-primary. And then we'll do our special class binding that we saw earlier in the course, we'll bind to class.btn-primary. That could be any class name and we want to set this only if onOffSwitch is set to on. So we're off and we get the light background. We click it on and our binding takes place. We get our btn-primary styling. So that looks a lot better to me. But you can go ahead and bind any class, however you'd like to style it. So that's all we're going to look at for toggle switches. In the next video we'll take a look at radio buttons.
Horizontal Radio Buttons
Let's look at radio buttons. We've already worked with traditional radio buttons in Bootstrap but here with ng2-bootstrap, we have a totally different styling. Again you can click the menu and go to the Buttons section, I'm already here. If we scroll down we can see that we're still using the same import that we used in the toggle switch video. So we already have this imported. So it's just a matter of looking at what we want to do with these radio buttons. You can see here that we have Left, Middle and Right and we can only click on one of those. Those are traditional radio buttons. But above it we have uncheckable radio buttons. Left is selected but if we click it again, everything's unselected and these are just simply toggles like we've already seen, they're just styled together as radio buttons. So let's take a look at how we create the markup for these radio buttons right here. Let's look right here, we can see a div and it has a class of btn-group. So btn-group is the Bootstrap class that'll give us the formatting we need. And inside of this btn-group div, we use labels. So we're not using buttons or inputs, we're using labels. So I'm going to go ahead and add this to our form. I fixed up our form and here we have our div with the class btn-group and we have two labels. These will be the radio buttons. So I gave each label a name and a ngModel of taxType. We'll have to add that variable to the component. And the first label is setting btnRadio to W2, and the second one is setting it to 1099. And again these are tax types within the United States for paying employees, or contractors. So this is set up very similar to the example from the website and it's showing up fine. If I click on W2, that stays down. If I click on 1099, W2 un-highlights, so that's good. Let's add taxType to our component and we'll see what value that gets set to. Let's just start off with W2 as the default. I'll refresh and W2 is selected. We can also print this out. So it is set to W2, and 1099 is set to 1099. And these work independently of the labels. Let's just change the labels just to make sure. We'll say Type W2 and Type 1099. So now as we select these buttons, our bound field taxType stays at W2 or at 1099, based upon this btnRadio directive. If you wanted to add another label, that's simple too. It probably makes more sense to have radio buttons of three or more, otherwise you could just use a toggle. So let's just call this other one Other. And there we have it, we have our three radio buttons. In the next video we'll take a look at ng2-bootstrap's rating control.
A Rating Control
Finally, we're going to look at ng2-bootstrap's rating control. You can see the example here, I got here by going to the menu and selecting Rating from the list of components. And here we can see an example of it. As we move the mouse over the ten-or-so stars, we can lock in a rating, can set to five stars, one star, ten stars. And there's a lot going on, like you can see the label to the right, that's not part of the control, that's part of the demo and the information below, I'll click on four for a rating of four, and we can see that the information updates in real time. And we can also use custom icons for ratings symbols. So let's get this working in our project. I'll scroll down to the Usage section, and we need to import RatingModule from the same place as always for ng2-bootstrap. So I'll copy that and I'll place that in our project. I'll go to the app.module.ts, and I'll take RatingModule and put it in the imports. So back to the website, let's see the markup. Here's our rating tag, so we'll use that with ngModel. And I'll go ahead and update our project to use this. So into our template I added a simple div with a class of form-group for styling and we have our rating tag. I'll give it a name of postRating and we'll also two-way bind to postRating. And that's not set up yet but it's working fine. It starts off with no rating, and we can select values one through five. Five seems to be the default. So let's add postRating to our component and we'll take a look at it. Here's a header just to show the postRating, and going to our component, we'll add postRating, and we'll just set it to, let's say five. I'll make sure everything's saved. So we get a rating of five and it does show us five stars. If I select the first star, we get a rating of one, and that seems to be working fine. Let's look at some of the properties for ratings. So max will let us set the maximum rating we can have. So yes, it does default to five. Let's change that to 10. So we'll set max to 10 as a property binding. And we get our 10 stars. That's working fine. We can set it to true for read-only mode. We can change titles and for stateOn and stateOff, we can also change the images that show. You can see here they're using glyph icons which come with Bootstrap 3, so let's actually use those. We'll set stateOn and stateOff to different values. Right now they're set to star and star-empty, but we can set them to glyphicon-check and glyphicon-unchecked. So stateOn is being set to glyphicon-check, and stateOff is set to glyphicon-unchecked. I'll save it, we'll get the refresh. And now we can see check boxes and unchecked boxes. And that's still working fine. There are also some events we can look at. There's an onHover event, it's said it's fired when icon selected, $event will be set to the rating and it says onLeave is set to fired when icon selected. Let's bind to these and let's see what they do. onHover and onLeave. So we use parenthesis for event binding, and the other event was onLeave. So let's add these methods, hover and leave, to our component. So for hover we'll just simply log out hover and the value and for leave we'll log out leave and the value, and this will show in the console, so let's make some room here. Okay I'll save this. And I'll hover over the check boxes, and you can see that as we hover, new messages are showing up. It just displays how many are selected as we hover. I'm not clicking at all, and then if I click on, say a rating of three, and I move my mouse out so we lose focus, you can see we get leave three. So that's our rating control and that's all we're going to look at for ng2-bootstrap. So we covered most of the form-related components here, and there are other ones you might want to check out for your user-interface outside of forms. So let's wrap up this module with a summary.
Summary and Course Wrap-up
In this module we worked with several controls in the ng2-bootstrap library. We looked at the Datepicker control and saw how to bind to dates. We saw several problems that come up with dates as properties and saw that binding to properties or a model object on the component, is the best way to go. We also saw the Timepicker control and learned how to work with some of its key properties. We added toggle buttons to our form and saw how to data-bind them and style them. We used the special binding class.class name to conditionally add and remove a class from the button. Next we looked at radio buttons styled in a horizontal row. This is a modern alternative for traditional radio buttons laid out vertically. Finally, we added a rating control to our form. We saw how to change the symbols used to display the control and we saw how to bind to several events. With that, this introductory course on Angular 2 forms is complete. I've enjoyed putting this course together, and you should have all the pieces to start building great forms for your Angular 2 applications. We focused exclusively on template-driven forms in this course, however there is an advanced technology for forms in Angular 2 that is beyond the scope of this course. The technology is called reactive forms. Here's a popular blog post on reactive forms by ThoughtRam that you may want to check out. Just check for reactive forms in Angular 2. I'm sure Pluralsight will cover reactive forms in an upcoming course and Google will document it on the angular.io site soon, it's not there yet. You would use reactive forms mainly for advanced validation, and also testing scenarios that don't require a dom. And that brings this course to a close. Good luck working with forms in Angular 2.
Course author
Mark Zamoyta
Mark started in the developer world over 25 years ago. He began his career with a Bachelor of Science in Computer Science from St. Johns University. After spending 10 years on Wall Street working...
Course info
LevelBeginner
Rating
(233)
My rating
Duration2h 39m
Released4 Nov 2016
Share course