1

This is my first time using AngularJS, and the form validation is making me question my sanity. You would think this would be the easy part, but no matter how many ways I've tried Googling, the only thing that works is if I set a flag inside my controller's submit if the form is invalid to set the error class. I've looked at similar problems here, but none of them helped, so please do not simply dismiss this as a potential duplicate. Everything else has been a fail.

In the example mark up below I have reduced my form down to just one element. Here is what I have observed:

  1. Using only $error.required does work. The ng-class { 'has-error' :registerForm.firstName.$error.required} does outline the text box with the bootstrap has-ertror class, but this is on form load, which I do not want.

  2. The <p> element with the error message will exhibit the same behavior, so I know that the message exists and is not malfored. It will also display if I only use $error.required. But as soon as I add && registerForm.$submitted ( or $isdirty or !notpristine ) the message will not display on form submit. There are no errors (have developers tools open in chrome) and will post to the web API with no problem and return ok 200 or 400 if I send bad params.

  3. I can write validation code inside my controller, checking if the field has a value and setting a flag on $scope such as $scope.firstNameIsRequired and that will work fine setting ng-show="$scope.firstNameIsRequired", but that will remove testability. So the problem definitely has to be with how I am adding this in the markup. But after a weekend spent googling I am at my wits end. The only other thing different is that I am using a span on a click element to submit the form instead of an input = submit, but the registerForm.$valid function is setting the correct value. Do I somehow need to trigger the form validation in that ng-click directive?

I am using angular.js v 1.4.8. I do have angular ui which has it's own validate, but that shouldn't interfere with the basic validation.

Here is the simplified markup:

<form name="registerForm" class="form-group form-group-sm" 
    ng-controller="userAccountController" novalidate>
<div class="form-group" 
        ng-class="{ 'has-error' : registerForm.firstName.$error.required }">
    <div><label>First Name</label> </div>
    <input type="text" class="form-control" id="firstName" name="firstName" value="" 
        ng-model="firstName" placeholder="First Name" maxlength="100" required=""/>
    <p ng-show="registerForm.firstName.$error.required && registerForm.$submitted" 
        class="alert alert-danger">First Name is required</p>
</div>  
    <div>
        <span class="btn btn-default" 
                ng-click="submit(registerForm.$valid)">Register</span>
    </div>

My controller code is

angular.module( "Application" ).controller( "userAccountController", [
"$scope", "userAccountService", function ( $scope, userAccountService)
{
    $scope.hasErrors = false;
    $scope.errorMessages = "";
    $scope.emailExists = true;
    $scope.clearErrors = function (){
        $scope.hasErrors = false;
    }

    $scope.onSuccess = function ( response ) {
        alert( "succeeded" );
    }

    $scope.submit = function (isValid) {
        if ($scope.registerForm.$invalid)
            return;
        alert("isvalid");
        $scope.clearErrors();
         var userProfile = $scope.createUser();
         userAccountService.registerUser(userProfile, $scope.onSuccess, $scope.onError);
    }

    $scope.createUser = function ()   {
        return {
            FirstName: $scope.firstName, LastName: $scope.lastName, Email: $scope.email,
            Password: $scope.password, SendAlerts: $scope.sendAlerts
        };
    };
}
]);

Any help will be appreciated. I probably just need a second set of eyes here because I have been dealing with this on and off since late Friday.

6
  • did you try ng-submit? Commented Dec 13, 2015 at 19:59
  • is this plnkr.co/edit/cXRBAvNTgneyFlwg5Qwe?p=preview work for you??? Commented Dec 13, 2015 at 20:10
  • Thanks, but no luck there either. Changed the span ng-click to a submit input and added ng-submit="submit(registerForm.$valid)" Commented Dec 13, 2015 at 20:13
  • did you check my plunker? Commented Dec 13, 2015 at 20:17
  • I just did. I am seeing the exact same behavior, the validation is displayed when the form loads. I am starting to wonder if it is the angular/bootstrap setup. Using grunt and npm is all new to me. Another dev on my team set that up and the karma test runner. Maybe there is something wrong there, perhaps a version conflict. When I asked him about angular form validation he said there wasn't any, so now I am really starting to wonder. I think I'll just hack this one using flags (I have that working onblur which is what I wanted) just so I can close the story and circle back later Commented Dec 13, 2015 at 20:33

1 Answer 1

1

in angular you want use the element.$valid to check wheter an model is valid or not - and you use element.$error.{type} to check for a specific validation error.

Keep in mind that the form.$submitted will only be set if the form is actually submitted - and if it has validationerrors it will not be submitted (and thus that flag is still false)

If you want to show errors only on submit you could use a button with type="submit" and bind to ng-click event - and use that to set a flag that the form has been validated. And handling the submit if the form is valid.

A short example with 2 textboxes, having required and minlength validation:

angular.module("myApp", [])
  .controller("myFormController", function($scope) {
    $scope.isValidated = false;
    $scope.submit = function(myForm) {
      $scope.isValidated = true;
      if(myForm.$valid) {
        console.log("SUCCESS!!");  
      }
    };
  });
.form-group {
  margin: 10px;
  padding: 10px;
}

.form-group.has-error {
  border: 1px solid red;  
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="myApp" ng-controller="myFormController">
  <form name="myForm">
    <div class="form-group" ng-class="{'has-error': myForm.name.$invalid && isValidated}">
      <span>Name:</span>
      <input type="text" name="name" minlength="5" ng-model="name" required />  
      <span ng-if="myForm.name.$error.required && isValidated">Name is required</span>
      <span ng-if="myForm.name.$error.minlength && isValidated">Length must be atleast 5 characters</span>
    </div>
    
    <div class="form-group" ng-class="{'has-error': myForm.email.$invalid && isValidated}">
      <span>Email:</span>
      <input type="text" name="email" minlength="5" ng-model="email" required />  
      <span ng-if="myForm.email.$error.required && isValidated">Email is required</span>
      <span ng-if="myForm.email.$error.minlength && isValidated">Length must be atleast 5 characters</span>
    </div>
    
    <button type="submit" ng-click="submit(myForm)">Submit</button>
  </form>
</div>

Sign up to request clarification or add additional context in comments.

9 Comments

I'll give you the best answer, because it does solve the initial issue. I'm not using an actual input type submit element, I am instead using an ng-click on a span, but that does show that the form is invalid (registerForm.$valid shows the correct value). But that was just the first step. My goal was to figure out why the !$pristine or $dirty was not working because I wanted to fire the validation for each element on blur as well as on submit. But without solving that one, I have to go with the ugly solution which is an expansion on your isValid, basically a custom !.$pristine for each field
As on odd aside, $pristine works inside my controller.
$scope.validateForm = function() { if ($scope.firstName === undefined || (!$scope.$pristine && $scope.firstName.length === 0) ) $scope.firstNameRequiredError = true; }
what you could do is creat a helper-directive that sets a specific field on the ng-model when you blur it, and just check for that field in the test. $dirty field is set as soon as a model is actually changed.
I may have to do that. I am finding this to be something I don't think I can solve in the mark up. I am now tracking both onblur and onfocus (onblur sets the input class to 'has-error' or 'has-success', reverting the input to no class on focus, and ensuring that I fire all validators on submit but only the one currently raising the onblur event so that I don't show errors for fields that the user hasn't touched yet unless it is a submit event. And on the post to the web api I still need to handle any 400 responses (eg user name taken) coming from model state that I'm not checking on theclient
|

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.