Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS form validation on dynamically generated input directives not working with ngForm

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

like image 358
mtpultz Avatar asked Feb 12 '23 02:02

mtpultz


2 Answers

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

like image 179
mtpultz Avatar answered Feb 14 '23 17:02

mtpultz


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.

like image 41
Mukund Kumar Avatar answered Feb 14 '23 15:02

Mukund Kumar