Set angularjs input directive name as a variable

2019-07-26 07:37发布

问题:

I'm trying to set an input like

<form name="myForm">
    <input name="{{ name }}"/>
</form>

It works in the dom. I see

<input name="whatever_name_is_set_to"/>

However in my ngForm I have

$scope.myForm:  { 
    {{ name }}:  {  } 
} 

Doh'

Why am I doing this? I'm trying to create a directive so that I can build my forms programmatically. Then I can do something like

<div my-field
        name="credits"
        field="course.credits"
        field-options="courseOptions.credits"
        title="Credits"
></div>

Plunker

回答1:

Updated 2017-03-23:

For AngularJS >= 1.3 interpolation is now supported in input names.


Original answer from 2014-06-05 (correct for AngularJS <= 1.2):

I just answered a similar question yesterday about dynamically named form elements. In short, no, you can't use interpolation to dynamically name your form fields - the interpolation string will end up in the field name as you have seen.

In your case you're probably going to need to look into dynamically compiling the input HTML inside your myField directive.

Here is a simplified example using $compile to dynamically generate form elements: http://jsfiddle.net/Sly_cardinal/XKYJ3/

HTML:

<div ng-app="myApp">
    <form name="myForm" ng-controller="myController">
        <div my-field 
             name="courseName"
             field="course.courseName"
             title="Course Name"></div>

        <div my-field 
             name="credits"
             field="course.credits"
             title="Credits"></div>

        <!-- Show that the values are bound. -->
        <pre>course: {{course | json:'  '}}</pre>
        <!-- Show that the field is being registered with the ngFormController. -->
        <pre>myForm.credits.$dirty: {{myForm.credits.$dirty}}</pre>
    </form>
</div>

JavaScript:

angular.module('myApp', [])
.controller('myController', ['$scope', function($scope){
    $scope.course = {
        credits: 100,
        courseName: 'Programming 201'
    };
}])
.directive('myField', ['$compile', '$parse', function($compile, $parse){
    // In a real project you'd probably want to use '$templateCache' instead
    // of having strings in your code.
    var tmpl = $compile('<label>{{title}}</label>');

    return {
        scope: true,
        link: function(scope, element, attr){
            scope.title = attr.title;

            var newEl = angular.element('<input type="text"/>');
            newEl.attr('ng-model', attr.field);
            newEl.attr('name', attr.name);

            tmpl(scope, function(fieldEl, scope){
                $compile(newEl[0].outerHTML)(scope, function(el, scope){
                    fieldEl.append(el);
                    element.append(fieldEl);
                });
            });
        }
    }
}]);

A note on this example:

This is a very specific situation - generating dynamic form elements - that requires the use of $compile. This is not the "go to" solution when working with Angular inputs and forms - Angular will handle all the common situations with directives, data-binding and everything else the framework provides. Plus, as Marc Kline's comment shows, it looks like at some point Angular will handle dynamic form management itself at some point in the future.

If you were to continue down the path using $compile to generate these form elements then you'd probably want to use the $templateCache to manage your templates so you're not trying to manage template strings inside your directive.



回答2:

Old question, but in case someone is looking for a way to do what question asked, you can create a directive that will dynamically create the name of the element after $compile'ing it.

An updated version of the answer that @Sly_cardinal posted is here: http://jsfiddle.net/XKYJ3/1/

HTML

<div ng-app="myApp">
    <form name="myForm" ng-controller="myController">
        <label for="{{ course.courseName.name }}" ng-bind="course.courseName.title"></label>
        <input id="{{ course.courseName.name }}" dynamic-input-name="course.courseName.name" ng-model="course.courseName.value" type="text" required />
        <br />
        <label for="{{ course.credits.name }}" ng-bind="course.credits.title"></label>
        <input id="{{ course.credits.name }}" dynamic-input-name="course.credits.name" ng-model="course.credits.value" type="number" required />

        <!-- Show that the values are bound. -->
        <pre>course: {{course | json:'  '}}</pre>
        <!-- Show that the field is being registered with the ngFormController. -->
        <pre>myForm.credits_field.$dirty: {{ myForm.credits_field.$dirty }}</pre>
    </form>
</div>

Javascript

angular.module('myApp', [])
.controller('myController', ['$scope', function($scope){
    $scope.course = {
        credits: {
            title: 'Credits',
            value: 100,
            name: 'credits_field'
        },
        courseName: {
            title: 'Course name',
            value: 'Programming 201',
            name: 'course_name_field'
        }
    };
}])
.directive('dynamicInputName', ['$compile', '$parse', function($compile, $parse){
    return {
        restrict: 'A',
        terminal: true,
        priority: 100000,
        link: function(scope, elem) {
            var name = $parse(elem.attr('dynamic-input-name'))(scope);
            elem.removeAttr('dynamic-input-name');
            elem.attr('name', name);
            $compile(elem)(scope);
        }
    };
}]);