FREELessons: 17Length: 2.7 hours

Next lesson playing in 5 seconds

  • Overview
  • Transcript

2.2 Form Field Directive: The Template

In the last screencast, we began creating our new contact form which our users will use to store a new contact in their list of contacts. In this screencast, we're going to create the first of two angular directives that we're going to use inside of our form. And the directive we're creating in this screencast is called the Form Field Directive. So currently, I'm here in our new.html view. You'll recall that this is the view that will be rendered when they're viewing the slash contact slash new url, and let's start by showing exactly what a call to our directive will look like when we're finished with it. Basically, what we wanna be able to do is create a new form field tag and this form field will have a record. In our case, that will be contact and it's gonna have field, in our case that will be firstName. This form field tag right here is gonna be replaced with the input element and all of the extra code that we're gonna need to set the first name field on our contact record. Remember, this contact here comes from our new controller. It's this contact right here, so you might think, well that looks really simple. Why don't we just use an input element with an NG model attribute, and it will get set automatically, but we could do that. But there's a little bit more we want to do as you go from the last screen cast. We want to do validation, and we want to pretty up our input element with some Twitter bootstrap classes. So, there's a little bit more to it than just replacing this with an input element. And you'll see exactly how that works. While I'm here, there are a couple of other attributes I'm actually gonna add. The first one is live='false' and the second one is required='true'. Required='true' should make sense. Basically, we want the firstName field, basically the firstName field is required on our contact, so it can't be left blank. And if it is left blank, then this form will be considered invalid when. When they click the create contact button. But what about live? The reason we have a live attribute here is because there are two places where we're gonna end up using this form field directive. One, of course, is here in our new contact form. We're also gonna use it when updating contract entries a little bit later on. And when we update contract entries, we want those changes to be saved as the user types them. We don't want them to have to press a update or save button. So if the user is changing a first name, so for example say the first name says Jim, we want them to be able to go backspace backspace James, and once the finish writing James it'll automatically be saved to the server. They don't have to press save or anything. Of course, that won't work when we're creating a new contact here because the contact doesn't exist yet in the database. And so, it can't be automatically updated when there's nothing on the back end to update. So we have to set live equal to false. So now that we have this form-field directive in here, we have to go ahead and build it. And I think the best place to start with this is in creating the template or HTML that will actually replace this form field element that we're adding here. So let's create a new template and we're gonna create this at public/views slash and we'll call it formField.HTML. So we're gonna start with a div and has a class of row and also a class of form group. This [UNKNOWN] is also gonna have the NG form attribute which is gonna have the value of field. Now, of course, notice that we're using double curly braces here to surround this field. So that means, this form attribute will actually be the value of our field here which is first name. Now, the reason we're doing this is because when we dynamically create input elements like this inside of our form here, our new contact form, it really makes things tricky for doing actual validation. So basically, what has to happen is the directive will validate itself. And then, if all the directives are valid the form will consider itself to be valid. And so for the directive to validate itself, and for us to check to its validity, we need to treat it as a form. And to do that, we add the ng-form attribute, to the directive. Now there's another attribute here, we wanna add. And this is the ng-class directive. Ng-class is basically an Angular version of just class. And this will only add certain classes if certain expressions are true. In our case, we only want to add the has error class if double curly brace field dot dirty is true, and double curly brace field dot dollarsign invalid is true. This may look like a mouthful, but if you look at it slowly, it should make sense. Basically, the class we're adding here has error, and this is a twitter bootstrap class that will change the color of the input element that we have inside this div if that input is considered invalid. Now we only want to add this has-error tag if the form that we're working with in here, and when we say field here, this is gonna be replaced remember with in this case, firstName. And in this case, firstName refers to the form, or in this case, the fake Angular form, if you will, that we're inside. So if the form is dirty, that mean one of the input elements inside of it has been changed. Of course, there's only going to be one input element in each one of these forms. But that's okay. If that input element has been changed in any way, it is considered dirty as opposed to pristine and if that field is invalid. Now, this should make sense, because remember the input element will start out as being blank. And in that case, it is invalid. But it's not dirty because the user hasn't typed anything into it yet. So you don't want them to see an error as soon as they load up the new form, right? It should only be considered an error if they press the Save button and that input is still invalid. Now, in the case of firstName here, invalid means that it. It is not filled in at all because it's not required so if it has been changed and then it still is invalid go ahead and mark it as having an error. And you'll see this behavior in action once we finish building this directive. Alright. Now, let's go ahead and add a label tag, and, of course, this label will display the field name. So we'll give it a few classes first. I'm gonna have column-small-2. We also want to add the class control label. Now the label, of course, is going to be the field name. And let's pipe it through the label piece filter that we created in a previous screen cast so that it looks more person friendly. After this, we wanna add a span tag here, and we wanna ng-if, meaning add this element in only if required is true. So, remember we have required here as being set to true. And so, we only want this span to be shown if required is true, and that's because this span is just going to add a little asterisk if the user knows that this is a required field. Next, let's add a div with a class of col-sm-6, and we also want to add an ng-switch attribute here. And this will switch based on required. Now, if you're not familiar with the ng-switch directive, think of it a lot like the switch statement in programming language. The way it works and programming language, is we can switch on a specific case. So in this case, that is required, right? So we want to do something based on the value you have acquired. And if it's the case that required is true, then we'll do one thing. And if it's the case that required is false, we can do something else. Now often switch will be used in a case where maybe the value may be more than just true or false. Maybe it's a number, and if it's one, two, three, four, five, you have a bunch of different values you want to do. In our case, we're switching on required. Because if this field is not required, we want to allow our user to remove it entirely from this contact. Obviously, they can't remove one that is required, so we won't show them the remove this field button if this is required. Now the way this works is inside of the element that has ng switc, we can have elements that have their own attributes. Either ng switch when or ng switch default. The first one here is going to be the input element that will be used if this is a required field. So we wanna say ng switch when and we'll say when true. So basically, this input element will only be showed when acquired equals true. Now this input element remember is supposed to change the field that we get passed in as an attribute on the record that we get passed in as another attribute. So what were gonna say here is ng-model and this is supposed to change the record and then [field]. And remember, since our fields are erased, we only want it to actually set the first value because the second value is the type, which is exactly what we also need to add here. So we have type= and this time we're doing this in curly braces. We have record[field][1]. We're not quite done here, we actually want to add a class here form dash control. Let's also go ahead and add the required attribute because, of course, this field is required as we know from our switch statement here. And finally, we have to add a couple actions, so we're gonna do ng change equals update. And we're gonna do ng blur equals, and we're gonna call this one blur update because these are slightly different functions. Now remember this is supposed to be multipurpose directive, and we want to have the option to update this right away every time a change is made to this input element. So, that's what this will do here. And similarly, that's what blur update will do. These are both going to update these immediately upon either a change being made, or when the input element is blurred, meaning when the user loses focus on that specific element. Now, inside these functions, we can check to see what the value of our live attribute here is. And if it's false, then, we will not actually perform these updates. But the functions still need to be run, just in case they are not live. And you might wonder, why do we have to perform the update, both on ng-change and on ng-blur? And the reason for this is relevant, not really to our new form here, but to the edit form that we're going to be using these directives on later on. And think of this scenario. You're looking at your list of contacts on the main page. You click one to update it, you go ahead and type in to edit it. Now, as you're typing, remember the update method will be called on every change, but we don't actually wanna send a request for every single character change. So what we do instead is have a set time out and it waits for you to finish typing. And then, once you have not typed another character for a full second, it will go ahead and do the update them. However, it's possible that it will take you less than a second to finish typing and then go click the Go Back to List button. And in that case, you'll see our list of contacts before the. The update has actually occurred. In that case, you may look at the field you just updated in the table and it hasn't been changed. So instead, we also do an update when the field is blurred. And in this case, when you stop typing and go click that back to list button. When you click that button, this input element is blurred and the update is preformed immediately. So by the time the list is displayed, the update will have taken place and you can see the new value in the table. Okay. This is all for the case where required is true, but we also need to manage the case where required is false. So for this, we're gonna have a div with a class of input-group. And this is going to be ng-switch-default. This will happen if required is not true. And in here, the first thing I'm gonna do is copy this input element and paste it down below. But I'm gonna take out ng-switch and I'm also gonna take our required. So we only have the model, the type, the class, ng-change and nd-blur underneath this, we want to put a span. And this has a class of input-group-btn or button. And this will be the button that shows up just after our input element. And so in here, we'll go ahead and add a button, and it will have the bootstrap classes of btn and btn-default. And we'll have the ng-click directive here, which will have remove, and it will take the field name as a parameter. And then inside this button here, we'll have a span and we're gonna use some glyph icons here to make this look kinda neat. The glyphicon class, the glyphicon-remove-circle. And this is just basically a circle with a little x in it. So that will look just fine. Now, we're just about done here. The only other thing we need to do is add our validation messages, right? Renew out some error messages if this field does not validate properly. And these are gonna be shown just after our input block here. Now, we wanna show these messages using a new Angular feature or Angular directive called ngMessages. So we have to come back to our project here. And we have to do bower install angular dash messages. Now, notice something a little bit different happened here. It says we're unable to find a suitable version for angular, so please choose one. Three different choices. The first thing you need to know is that bower keeps different copies of all the different versions of these packages, which is, of course, important for maintaining older projects. However, the other thing that I did not tell you is that angular-messages is a feature which is only supported in angular one point three. When we originally installed angular, we instilled angular one point two, which is the latest stable version of angular. However, angular one point three is available in beta versions, and that's what we're going to have to use if we want to use angular messages. And that's fine. As you can see, we have a version of angular. One point three here as version number three that we're being offered. So, I'm going to go ahead and type in three as my answer and I'll hit enter, and now you can see that not only has angular messages been installed, but our version of angular has been replaced by angular 1.3. Now, we don't have to make any changes because the path to our angular 1.3 version will be identical to our path to Angular 1.2 which we currently have. So that is one of the really nice features of Bower is that it will go ahead and replace that for us and it knows to look at the version dependencies that can get that all sorted out for us. Now, of course, we'll have to include messages in our public/main.html page. Right down here, I'll copy resource there and I'll change this to lib angular-messages. There we go. The last place we have to update is in app.js, where we import route and resource. We also have to import ngMessages. And now, we can come back to our form field directives template here, and let's add one more div here, and this div will have a class of col-sm-four and it will also have a class of has error. Now, of course, this is not going to be shown all the time. It's only going to be shown, we'll do ng-show and we will only show this and let me copy this bullion expression up here. And I'm going to paste it in there because it's only going to be shown if this form is dirty and if it is invalid. Now, because we're using ng messages, we have to add ng-messages and the messages is going to be based on field.$error. Now, the way ng messages works is the error message here is either going to be. It required if the valuation error is that our required field was not filled in. Otherwise, it's gonna be the name of a type here. So if we have a URL field and the value typed in is not a URL, the error message we're gonna get is URL. What we're going to do here is have a bunch of error messages, so we can have a paragraph with a class controlled dash label, and then we have the ng-messaage attribute, and this directive takes the knee of an error, so in this case it's going to be required. And so, if ng-message is up here, if the error is a required error, this is the paragraph that will be shown. Right, so in here, we'll say field and we'll go ahead and label case it, is required. That's all we need to do. And now, we need to add another one of these paragraphs for every single type of error that could possibly occur. Now that is actually quite a list. And, of course, we don't want to do this manually, we'll use angular to make to make it easier. So let's go ahead and open up our public slash source, and we're going to create a new file here called directives.js. And, of course, we have to get our angular module. And the first thing we want to do here is not create a directive. But instead, it's create a value. Now this value is going to be called, field types. And the reason we're creating this as a value is just because we're going to use it in both of the directives that are going to show up in this file. And this is going to be a simple object. But what is in this object is a little bit more than simple. Basically, this is a list of all of our fields and the error messages they should get. First of all, as the keys in this object is the proper name for the input types, we have text, e-mail all the way down to color and these are all of the HTML5 input types that we can use. Their values are a two value [UNKNOWN] here. The first value is the way you would display this value if it was being printed out to a user. So, for example, text is just text with a capital T, but URL is URL, all in capitals, and tel, t-e-l, is actually phone number. And then, the second value is the error message that would be displayed for a given field, so we might say first name should be text, or home phone. Should be phone number, should be a phone number. And so,this just makes sure we have grammatically correct error messages. Now, this object here is going to be available in our form field directive. So, underneath our required paragraph, here, we can create another paragraph. Again, with control-label, and this is going to do an ng repeat and it is going to loop over that object. So the way you loop over an object is we can say key and value in and this will be types. So we have the key in value and types and the ng dash message is of course going to be the key, right. Because it's going to be one of this HTML5 input types. So the message is going to be the key. And the field, of course, will be the same here. We can say field and we will pipe that to label case and then after that we want to have value one. And this, of course, is the value from the type here, and so this going to to be the error message here. So this could say something like the field name if it's home phone, it will say home phone should be a phone number. So there you go, this is the whole template for our form field directive. So in the next screencast, we'll go ahead and write the JavaScript that will wire up that form field directive, and then we can actually use in our new contact form.

Back to the top