67

Background: I am currently working on an application with tabs; and I'd like to list the fields / sections that fail validation, to direct the user to look for errors in the right tab.

So I tried to leverage form.$error to do so; yet I don't fully get it working.

If validation errors occur inside a ng-repeat, e.g.:

<div ng-repeat="url in urls" ng-form="form">
  <input name="inumber" required ng-model="url" />
  <br />
</div>

Empty values result in form.$error containing the following:

{ "required": [
  {
    "inumber": {}
  },
  {
    "inumber": {}
  }
] }

On the other hand, if validation errors occur outside this ng-repeat:

<input ng-model="name" name="iname" required="true" />

The form.$error object contains the following:

{ "required": [ {} ] }

yet, I'd expect the following:

{ "required": [ {'iname': {} } ] }

Any ideas on why the name of the element is missing?

A running plunkr can be found here: http://plnkr.co/x6wQMp

7
  • 2
    I noticed this the other day. It doesn't make sense to me because you can inspect form.iname.$error and get the correct values. What I ended up doing was using form.$valid to check validity and enable submit button instead. Commented Mar 11, 2014 at 12:29
  • Hi Ed thanks a lot for your response. I agree, though I'd like to tell the user which control is failing to validate. The form is inside a tabbed view, so it is easy to miss that you can not save because there is a validation error inside an repeat in a tab you are currently not viewing. (I hope my explanation was good enough to follow :)) Commented Mar 11, 2014 at 13:25
  • Yeah I agree it's good to show the user what's valid and what isn't. One option is that you can have error text in <span> tags for every possible error and use ng-show="form.fieldName.$error.required" for example. You could then either inline those with the inputs, or put them near the submit button. It's definitely not ideal though - I wonder if the unpopulated $error value is a bug with angular? Commented Mar 11, 2014 at 14:10
  • 8
    I believe $error is populated, you just can't dump it out as JSON nicely. Take a look here: plnkr.co/edit/Z47J2W?p=preview The error types can all be iterated. Commented Mar 11, 2014 at 14:43
  • c0bra: thanks, I'll try to follow your solution, looks like it does exactly what I need. Commented Mar 11, 2014 at 15:39

4 Answers 4

130

As @c0bra pointed out in the comments the form.$error object is populated, it just doesn't like being dumped out as JSON.

Looping through form.$errors and it's nested objects will get the desired result however.

<ul>
  <li ng-repeat="(key, errors) in form.$error track by $index"> <strong>{{ key }}</strong> errors
    <ul>
      <li ng-repeat="e in errors">{{ e.$name }} has an error: <strong>{{ key }}</strong>.</li>
    </ul>
  </li>
</ul>

All the credit goes to c0bra on this.

Another option is to use one of the solutions from this question to assign unique names to the dynamically created inputs.

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

1 Comment

Note: Tags must have name attribute for it to return names. If you tend not to assign name tags, coz angular, it will return blanks at e.$name
5

I made a function that you pass the form to. If there are form errors it will display them in the console. It shows the objects so you can take a look. I put this in my save function.

function formErrors(form){
  var errors = [];
  for(var key in form.$error){
    errors.push(key + "=" + form.$error);
  }
  if(errors.length > 0){
    console.log("Form Has Errors");
    console.log(form.$error);
  }
};

Comments

3

Brett DeWoody's answer is correct. I wanted to do the logic in my controller though. So I wrote the below, which is based off of the answer user5045936 gave. This may also help some of you who want to go the controller route. By the way Im using the toaster directive to show my users validation messages.

 if (!vm.myForm.$valid) {
            var errors = [];

            for (var key in vm.myForm.$error) {
                for (var index = 0; index < vm.myForm.$error[key].length; index++) {
                    errors.push(vm.myForm.$error[key][index].$name + ' is required.');
                }
            }

            toaster.pop('warning', 'Information Missing', 'The ' + errors[0]);

            return;
        }

Comments

2

If you have nested forms then you will find this helpful:

 function touchErrorFields(form) {
    angular.forEach(form.$error, function (field) {
      angular.forEach(field, function(errorField) {
        if (!errorField.hasOwnProperty('$setTouched')) {
          touchErrorFields(errorField);
        } else {
          errorField.$setTouched();
        }
      })
    });
  }

3 Comments

This solution is perfect to recursively show errors on nested form, thank you !
@NicolasLeucci, you are welcome! happy that it helped you
With angularjs 1.6.2, errorField.hasOwnProperty('$setTouched') returns false.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.