Monday, November 19, 2012

Angular JS: Form Validation

Basic form validation is already done for you in Angular JS... if you just know how to use it. Form validation in Angular is all done by directives, which means you just wire it up in markup, and you don't need to write a "validation function" in your controller to handle any of that dirty work.

Angular has wired up most form elements themselves to be directives, so <input type="email"/> will validate an email address and so on. Angular also looks for attributes like required  and handles them appropriately.

Let's have a look at some basic form validation in Angular:

The form

<form name="mainForm" ng-submit="sendForm()">
    <div>
      <label for="firstName">First Name</label>
      <input id="firstName" name="firstName" type="text" ng-model="person.firstName" required/>
      <span class="error" ng-show="mainForm.firstName.$error.required">required</span>
    </div>
    <div>
      <label for="lastName">Last Name</label>
      <input id="lastName" name="lastName" type="text" ng-model="person.lastName" required/>
      <span class="error" ng-show="mainForm.lastName.$error.required">required</span>
    </div>
    <div>
      <label for="email">Email</label>
      <input id="email" name="email" type="email" ng-model="person.email" required/>
      <span class="error" ng-show="mainForm.email.$error.required">required</span>
      <span class="error" ng-show="mainForm.email.$error.email">invalid email</span>
    </div>
    <div>
      <input type="checkbox" ng-model="agreedToTerms" 
        name="agreedToTerms" id="agreedToTerms" required/>
      <label for="agreedToTerms">I agree to the terms</label>
      <span class="error" ng-show="mainForm.agreedToTerms.$error.required">You must agree to the terms</span>
    </div>
    <div>
      <button type="submit">Send Form</button>
    </div>
  </form>


Here's a Plunker of the code at work:
NOTE: Plunker does not like Safari or IE, if you're seeing odd behavior in those browsers, there you go.



As you can see above, the validation information changes dynamically as you enter data, thanks to directives.

A few things to know about form validation in Angular:



  • ng-submit cannot be called until the entire form is $valid.
  • All validation and form data is actually stored in the scope under $scope['myFormName']. This is why you can write it out with Angular binding.
  • Access to validation information can be found via: $scope.nameOfForm.nameOfField. This means adding a name="" attribute to your inputs is very important.
  • Angular will automatically add CSS classes: ng-valid, ng-invalid, ng-dirty and ng-pristine to DOM elements to reflect their state.
There is also a lot of important information to be found in the documentation for the input directive.


EDIT: Additional information can be found in these blog entries:


15 comments:

  1. Thank you for this!

    ReplyDelete
  2. Very helpful and nicely done, thank you for posting!

    ReplyDelete
  3. Your email validator is incorrect. 5 level TLDs are valid, for instance see .travel. That fails your validation.

    ReplyDelete
    Replies
    1. Well, it's not really "my" email validator. It's part of Angular's core library. But you should fork it on github and fix it, then submit a pull request. Open source FTW.

      Delete
  4. I'm a bit confused here, as I think you're incorrect when you say "ng-submit cannot be called until the entire form is $valid.". On my browser (google chrome 26.0.1410.65) you can submit fine, as long as the browser is happy - angular doesn't have a say in the matter.

    To show you what I mean, make everything valid, except the email - here put a@b.c - this is valid by the browser, but not by angular - you can see that in your divs at the bottom. For me, that submits, even though its invalid. You can also see this with the "novalidate" directive in the form tag - now you can submit when everything is invalid!

    I'm trying to find a nice way of preventing that at the moment, but I'm new to Angular, so finding it hard going!

    ReplyDelete
    Replies
    1. So I feel I need to go back and add a few things to this blog entry. There are a few situations in which forms in Angular behave in counter-intuitive ways. One of them is a situation where you only have one input in your form... being invalid will not matter in this scenario, it will still submit. This is, apparently, to conform to HTML specs. The other situation that breaks forms in angular is if you have an action="" attribute on your form tag. In that case it will post (or get) to whatever is specified in your action, and I don't believe it will care if it's invalid or not (I'll have to double check, though).

      Delete
  5. Have you tested these code in Safari or iOs.

    It seems like "required" is not supported by Safari and angular js is failing to validate "required" in Safari and iOs

    ReplyDelete
    Replies
    1. I haven't seen that, then again, I'm not sure when the last time I've tested in Safari was... Do you have a plunk that shows it not working? I suspect that maybe you're testing it in a form with only one input, in which case Angular allows you to submit even if it's invalid, per HTML5 specs (weird I know).

      Delete
    2. I am seeing this also. Need to get an example up and bug filed in GitHub (I do not think there is one open yet).

      Delete
    3. FYI: Bug filed.

      https://github.com/angular/angular.js/issues/3205

      Delete
    4. There isn't a bug in Angular, I just tested form validation in Safari and it works fine. HOWEVER: Safari doesn't like Plunker, so that may be what you're seeing.

      Here's a Gist of the file I used to test in Safari. worked just fine.

      Delete
  6. hi, How if we have 2 submit button.

    For example 1 button to submit normaly, and the other to submit with redirect parameter.

    i want to disable the submit button while the form not valid yet.

    ReplyDelete
  7. Thanks a lot Ben! Excellent entry! :)
    It really helped me understand form validation in AngularJs.

    ReplyDelete
  8. I think it's bad design for a form to start in an error state. See the following for an alternate opinion: http://thedunlops.id.au/content/index.php?option=com_content&view=article&id=72&Itemid=88

    ReplyDelete
    Replies
    1. Well, it's different UX, I'm not sure if it's "bad design". Arguably not telling a user that they've missed a required field until after they thought they've filled out the entire form, only to make them go back and fill out the missing fields is probably bad usability. I came from MVC myself, and I originally thought the same way because it was different than what I was used to. But once I really thought about it, I realized it's more a matter of feedback. If you use $dirty and $error.required in concert, it can be a pretty effective tool.

      Delete

This form allows some basic HTML. It will only create links if you wrap the URL in an anchor tag (Sorry, it's the Blogger default)