AngularJS: Fields added dynamically are not registered on FormController

16,732

Solution 1

Update 7/31/2015 This has been fixed since 1.3, see here: https://github.com/angular/angular.js/issues/1404#issuecomment-125805732

Original Answer This is unfortunately a short coming of AngularJS at the moment. Angular's form validation doesn't work with dynamically named fields. You can add the following at the bottom of your HTML to see exactly what's going on:

<pre>{{myForm|json}}</pre>

As you can see, Angular isn't getting the dynamic input name right. There's currently a work around involving nested forms that can get kind of nasty, but it does work and (with a little extra work) will submit the parent form without trouble.

If you want, you can go drum up more support for the issue: GitHub Issue - dynamic element validation. Either way, here's the code:

http://jsfiddle.net/langdonx/6H8Xx/2/

HTML:

<div data-ng-app>
    <div data-ng-controller="MyController">
        <form id="my_form" name="my_form" action="/echo/jsonp/" method="get">
            <div data-ng-repeat="field in form.data.fields">
                <ng-form name="form">
                    <label for="{{ field.name }}">{{ field.label }}:</label>
                    <input type="text" id="{{ field.name }}" name="{{field.name}}" data-ng-model="field.data" required>
                    <div class="validation_error" data-ng-show="form['\{\{field.name\}\}'].$error.required">Can't be empty.</div>

                </ng-form>
            </div>
            <input type="submit" />
        </form>
    </div>
</div>

JavaScript:

MyController.$inject = ["$scope"];

function MyController($scope) {
    $scope.form = {};
    $scope.form.data = {};
    $scope.form.data.fields = []

    var f1 = {
        "name": "input_1",
        "label": "My Label 1",
        "data": ""
    };
    var f2 = {
        "name": "input_2",
        "label": "My Label 2",
        "data": ""
    };

    $scope.form.data.fields.push(f1);
    $scope.form.data.fields.push(f2);
}

Solution 2

I ran into a similar problem myself and what i did to work around it was to place the name of the field before calling $compile on the template. A simple string.replace did the trick. Then again that was only possible because i was getting the field templates in through http and had access to the template text.

update: here is a fiddle with a little hack to make your example work

app.directive('field', function($compile) {
    var linker= function(scope, element){

    var template = '<input type="text" name="{{fname}}" ng-model="model">'
    .replace('{{fname}}', scope.fname);
        element.html(template)
        $compile(element.contents())(scope)
    }
  return {
    restrict: 'E',
    scope: {
      fname: '=',
      model: '='
    },

    replace: true,
      link: linker
  };
});

http://jsfiddle.net/2Ljgfsg9/4/

Share:
16,732
AngularChef
Author by

AngularChef

Formations et tutos Angular en français - https://www.angularchef.com/

Updated on June 07, 2022

Comments

  • AngularChef
    AngularChef about 2 years

    I have the following static form in AngularJS:

    <form name="myForm" class="form-horizontal">
    
      <label>First Name:</label>
      <input type="text" name="first_name" ng-model="entity.first_name">
    
      <label>Last Name:</label>
      <input type="text" name="last_name" ng-model="entity.last_name">
    
    </form>
    

    Angular creates a FormController for me and publishes it into the scope (under the form name). Which means I have access to properties like the following:

    $scope.myForm.first_name.$error
    $scope.myForm.last_name.$invalid
    ...
    

    This is super useful!

    But in my case I'm building a form dynamically, using directives:

    <form name="myForm" class="form-horizontal">
    
      <field which="first_name"></field>
      <field which="last_name"></field>
    
    </form>
    

    The <field> directives don't resolve to actual <input> elements until after a while (after I've fetched some data from the server, linked the directives, etc.).

    The problem here is that no field properties are defined on the form controller, as if dynamic fields didn't register with the FormController:

    // The following properties are UNDEFINED (but $scope.myForm exists)
    $scope.myForm.first_name
    $scope.myForm.last_name
    

    Any idea why? Any solution/workaround?

    You can see the entire code in this jsFiddle:
    http://jsfiddle.net/vincedo/3wcYV/