4

I'm trying to validate dynamic form input generated using a directive and isolate scopes. Outside the directive I've got the main form, and inside the ng-repeat I'm trying to use ng-form, but it doesn't ever show the error messages. Initially I had the ng-form on ng-repeat, but figured that wouldn't work since it was out of scope in the directive, so placed it on the parent div of the directive, but still doesn't show validation on invalid email.

Form with ng-repeat and field directive

<form name="mainForm" 
      role="form" 
      ng-controller="WizardFormController as wizFormCtrl" 
      ng-submit="submit( mainForm.$valid )"
      novalidate>

      <div ng-repeat="field in panel.form_fields">

           <form-field field="field" 
                       model="models[field.field_name]" ng-form="subForm">
           </form-field>

      </div>

      <div class="form-group clearfix">
      <button class="btn btn-primary pull-right" 
              ng-click="update( models )"
              ng-disabled="mainForm.$invalid">Save Progress</button>

      </div>

</form>

Form Field Directive

<div class="form-group" ng-form="subForm">

    <label for="{{field.field_name}}">{{field.field_label}}</label>

    <input type="text"
       class="form-control"
       id="{{field.field_id}}"
       name="{{field.field_name}}"
       ng-model="model">

     <div ng-show="subForm[field.field_name].$dirty &&
              subForm[field.field_name].$invalid">Invalid:

    <span ng-show="subForm[field.field_name].$error.email">This is not a valid email.</span>
    </div>

</div>

Looks like it should work looking at the generated markup indicating the field is ng-valid:

<div class="form-group ng-scope ng-dirty ng-valid-required ng-valid ng-valid-email" 
     ng-form="subForm">

Is it just how I'm accessing subForm:

subForm[field.field_name].$dirty

UPDATE I found the work around for this and answered it below, see here

1
  • Can you post your directive JS code as well as a demo fiddler/plnkr? Commented Sep 3, 2014 at 21:51

2 Answers 2

7

The solution for this issue is apparently in the works, and has been an issue for 2+ years. Add your vote to it on GitHub! The easiest solution to implement and arguably the best solution can be found here with credit to Thinkscape, and I've copied it below.

  angular.module('interpol', [])

  .config(function($provide) {

    $provide.decorator('ngModelDirective', function($delegate) {
      var ngModel = $delegate[0], controller = ngModel.controller;
      ngModel.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
        var $interpolate = $injector.get('$interpolate');
        attrs.$set('name', $interpolate(attrs.name || '')(scope));
        $injector.invoke(controller, this, {
          '$scope': scope,
          '$element': element,
          '$attrs': attrs
        });
      }];
      return $delegate;
    });

    $provide.decorator('formDirective', function($delegate) {
      var form = $delegate[0], controller = form.controller;
      form.controller = ['$scope', '$element', '$attrs', '$injector', function(scope, element, attrs, $injector) {
        var $interpolate = $injector.get('$interpolate');
        attrs.$set('name', $interpolate(attrs.name || attrs.ngForm || '')(scope));
        $injector.invoke(controller, this, {
          '$scope': scope,
          '$element': element,
          '$attrs': attrs
        });
      }];
      return $delegate;
    });
  })

  .run(function($rootScope) {
    $rootScope.models = [{
      value: 'foo'
    },{
      value: 'bar'
    },{
      value: 'baz'
    }];
});

I just dropped it in, marked it as a dependency, and the form in my question works.

Cheers

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

2 Comments

Though how this actually works is a mystery I'm too new to AngularJS. Anyone interested in giving a high-level description of the above solution?
Looks like it's been addressed since this answer was written: link and you can now accomplish dynamic form validation without workarounds
1

use this as a Form Field Directive:

<div class="form-group" ng-form="subForm">

    <label for="{{field.field_name}}">{{field.field_label}}</label>

    <input type="email"
       class="form-control"
       id="formid"
       name="formname"
       ng-model="model">

     <div ng-show="subForm.formname.$dirty &&
              subForm.formname.$invalid">Invalid:

    <span ng-show="subForm.formname.$error.email">This is not a valid email.</span>
    </div>

</div>

ng-form itself handle field name. you cant put name="{{field.field_name}}" like this.

5 Comments

Hi, what if I can't avoid this since we're generating large amounts of forms using a form-field directive and json, so the only hard-coded bit is the type, and we provide all the different html input types and select as templates. Is there another way to do this maybe not using ngForm that was just a way I found through the AngularJS docs and some tutorials.
i dont think there is an alternative for this. dynamically generated form field can only be validated through this.
It does validate in the parent form properly, just not on a specific input allowing me to show input errors. I'll keep digging and see if there is solution of some sort. Thanks
just put type="email" for validate email form. i think it will work properly for specific input field also. just use edited code
Hey, I found a solution. This particular issue has a few different solutions that have been amalgamated over the last 2 years that can be found on GitHub (github.com/angular/angular.js/issues/1404). The easiest solution to drop in is this one: plnkr.co/edit/hSMzWC?p=preview. Just add it as a dependency and the code in the example above just works.

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.