Динамическая проверка и имя в форме с помощью AngularJS
У меня есть эта форма: http://jsfiddle.net/dfJeN/
Как вы видите, значение имени для ввода статически установлено:
name="username"
проверка формы отлично работает (добавьте что-нибудь и удалите весь текст с ввода, появится текст).
Затем я пытаюсь динамически установить значение имени: http://jsfiddle.net/jNWB8/
name="{input.name}"
Затем я применяю это к моей проверке
login.{{input.name}}.$error.required
(этот шаблон будет использоваться в ng-повторе), но моя проверка формы нарушена. Он правильно интерпретируется в моем браузере (если я проверяю элемент, я видел login.username. $Error.required).
Любая идея?
РЕДАКТИРОВАТЬ: После регистрации области в консоли появляется, что
{{input.name}}
выражение не интерполируется. Моя форма как {{input.name}}, но не имя пользователя.
UPDATE: Поскольку 1.3.0-rc.3 name= "{{input.name}}" работает как ожидалось. См. # 1404
Ответы
Ответ 1
Вы не можете делать то, что вы пытаетесь сделать таким образом.
Предполагая, что вы пытаетесь сделать, вам нужно динамически добавлять элементы в форму с чем-то вроде ng-repeat, вам нужно использовать вложенные ng-form разрешить проверку этих отдельных элементов:
<form name="outerForm">
<div ng-repeat="item in items">
<ng-form name="innerForm">
<input type="text" name="foo" ng-model="item.foo" />
<span ng-show="innerForm.foo.$error.required">required</span>
</ng-form>
</div>
<input type="submit" ng-disabled="outerForm.$invalid" />
</form>
К сожалению, это не очень хорошо документированная функция Angular.
Ответ 2
Использование вложенного ngForm позволяет вам получить доступ к определенному InputController из HTML-шаблона. Однако, если вы хотите получить к нему доступ с другого контроллера, это не поможет.
например.
<script>
function OuterController($scope) {
$scope.inputName = 'dynamicName';
$scope.doStuff = function() {
console.log($scope.formName.dynamicName); // undefined
console.log($scope.formName.staticName); // InputController
}
}
</script>
<div controller='OuterController'>
<form name='myForm'>
<input name='{{ inputName }}' />
<input name='staticName' />
</form>
<a ng-click='doStuff()'>Click</a>
</div>
Я использую эту директиву, чтобы помочь решить проблему:
angular.module('test').directive('dynamicName', function($compile, $parse) {
return {
restrict: 'A',
terminal: true,
priority: 100000,
link: function(scope, elem) {
var name = $parse(elem.attr('dynamic-name'))(scope);
// $interpolate() will support things like 'skill'+skill.id where parse will not
elem.removeAttr('dynamic-name');
elem.attr('name', name);
$compile(elem)(scope);
}
};
});
Теперь вы используете динамические имена везде, где требуется только атрибут "dynamic-name" вместо атрибута "name".
например.
<script>
function OuterController($scope) {
$scope.inputName = 'dynamicName';
$scope.doStuff = function() {
console.log($scope.formName.dynamicName); // InputController
console.log($scope.formName.staticName); // InputController
}
}
</script>
<div controller='OuterController'>
<form name='myForm'>
<input dynamic-name='inputName' />
<input name='staticName' />
</form>
<a ng-click='doStuff()'>Click</a>
</div>
Ответ 3
Проблема должна быть исправлена в AngularJS 1.3, в соответствии с этим обсуждением Github.
Между тем, здесь временное решение, созданное @caitp и @Thinkscape:
// Workaround for bug #1404
// https://github.com/angular/angular.js/issues/1404
// Source: http://plnkr.co/edit/hSMzWC?p=preview
app.config(['$provide', 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;
});
}]);
Демо на JSFiddle.
Ответ 4
Приятный один by @EnISeeK.... но я получил его более элегантным и менее навязчивым для других директив:
.directive("dynamicName",[function(){
return {
restrict:"A",
require: ['ngModel', '^form'],
link:function(scope,element,attrs,ctrls){
ctrls[0].$name = scope.$eval(attrs.dynamicName) || attrs.dynamicName;
ctrls[1].$addControl(ctrls[0]);
}
};
}])
Ответ 5
Небольшое улучшение по сравнению с решением EnlSeek
angular.module('test').directive('dynamicName', ["$parse", function($parse) {
return {
restrict: 'A',
priority: 10000,
controller : ["$scope", "$element", "$attrs",
function($scope, $element, $attrs){
var name = $parse($attrs.dynamicName)($scope);
delete($attrs['dynamicName']);
$element.removeAttr('data-dynamic-name');
$element.removeAttr('dynamic-name');
$attrs.$set("name", name);
}]
};
}]);
Вот пробная версия plunker. Подробное описание
Ответ 6
Я немного расширю решение @caitp и @Thinkscape, чтобы создать динамически созданное вложенное ng-forms, например:
<div ng-controller="ctrl">
<ng-form name="form">
<input type="text" ng-model="static" name="static"/>
<div ng-repeat="df in dynamicForms">
<ng-form name="form{{df.id}}">
<input type="text" ng-model="df.sub" name="sub"/>
<div>Dirty: <span ng-bind="form{{df.id}}.$dirty"></span></div>
</ng-form>
</div>
<div><button ng-click="consoleLog()">Console Log</button></div>
<div>Dirty: <span ng-bind="form.$dirty"></span></div>
</ng-form>
</div>
Вот мое демо на JSFiddle.
Ответ 7
Я использовал решение Ben Lesh, и он работает хорошо для меня. Но одна проблема, с которой я столкнулся, заключалась в том, что когда я добавил внутреннюю форму с помощью ng-form
, все состояния формы, например. form.$valid, form.$error
и т.д. стал undefined, если я использовал директиву ng-submit
.
Итак, если бы у меня было это, например:
<form novalidate ng-submit="saveRecord()" name="outerForm">
<!--parts of the outer form-->
<ng-form name="inner-form">
<input name="someInput">
</ng-form>
<button type="submit">Submit</button>
</form>
И в моем контроллере:
$scope.saveRecord = function() {
outerForm.$valid // this is undefined
}
Поэтому мне пришлось вернуться к использованию обычного события click для отправки формы, и в этом случае необходимо передать объект формы:
<form novalidate name="outerForm"> <!--remove the ng-submit directive-->
<!--parts of the outer form-->
<ng-form name="inner-form">
<input name="someInput">
</ng-form>
<button type="submit" ng-click="saveRecord(outerForm)">Submit</button>
</form>
И пересмотренный метод контроллера:
$scope.saveRecord = function(outerForm) {
outerForm.$valid // this works
}
Я не совсем понимаю, почему это так, но, надеюсь, это помогает кому-то.
Ответ 8
Эта проблема исправлена в Angular 1.3+
Это правильный синтаксис для того, что вы пытаетесь сделать:
login[input.name].$invalid
Ответ 9
если мы установим динамическое имя для входа, как показано ниже
<input name="{{dynamicInputName}}" />
Затем мы используем проверку набора для динамического имени, как показано ниже.
<div ng-messages="login.dynamicInputName.$error">
<div ng-message="required">
</div>
</div>