问题
I want to have some interaction of the label for a field with the field validation properties. I created a plunker of a working example.
http://plnkr.co/edit/oNq5lmjtjRDaEkzKsrR2?p=preview
But in this example the fieldname is hardcoded.
I tryed to set the fieldname via scope variable into the template. This is not working:
http://plnkr.co/edit/yEl04xyFR5RWYCmI2bMH?p=preview
HTML:
<form name="myForm" ng-controller="ctrl">
<fieldset field-name="{{model.fieldName}}" label-text="{{model.label}}" ng-model="model.value">
<fieldset>
</form>
JavaScript:
var app = angular.module('myApp',[]);
app.directive('fieldset', function(){
return {
template: '<label ng-class="{invalid:$parent.myForm[{{fieldName}}].$invalid}">{{lbl}}</label>: ' +
'<input name="{{fieldName}}" ng-model="value" required>',
restrict : 'E',
scope : {
value : '=ngModel',
lbl : '@labelText',
fieldName : '@'
},
link : function(scope, elem, attrs) {
console.log(scope.$parent.myForm);
}
}
})
app.controller('ctrl', function($scope) {
$scope.model = {
fieldName : 'myField',
label : 'Label for Field',
value : 6
}
});
The fieldname in scope.$parent.myForm is '{{fieldName}}' instead of 'myField'. But in DOM the fieldname is assigned as expected. How can I solve this problem?
回答1:
Stewie is absolutely right. If the ng-model directive is created it will immediately be registered at the form. If you have a look at the angular sources you will find that the ngModels are registered by their current name in the array form
:
if (control.$name) {
form[control.$name] = control;
}
In your case this is '{{fieldName}}'. This name
can not be changed, because it is the key in the array. So what can we do to change this? If we have a look at the FormController API we can see there are functions to remove ($removeControl
) or add ($addControl
) controls. Both functions require the NgModelController as input parameter. E.g. the NgModelController that angular has created for our input element. We have access to this controller by:
var modelController = inputElement.controller('ngModel');
I think we have now all information together to write the directive:
app.directive('fieldset', function(){
return {
template: '<label ng-class="{invalid:form[fieldName].$invalid}">{{lbl}}</label>: ' +
'<input name="{{fieldName}}" ng-model="value" required>',
restrict : 'E',
require: '^form', // we need the parent NgFormController
scope : {
value : '=ngModel',
lbl : '@labelText',
fieldName : '@'
},
link : function(scope, elem, attrs, formCtrl) {
// get the NgModelController for the input element
var modelCtrl = elem.find('input').controller('ngModel');
// remove the ModelController from the FormController
formCtrl.$removeControl(modelCtrl);
// set the right name
modelCtrl.$name = scope.fieldName;
// add the namend ModelController to the FormController
formCtrl.$addControl(modelCtrl);
// publish the FormController to the scope - so we don't need to mess around with the parent scope.
scope.form = formCtrl;
}
}
})
I would also suggest publish the NgFormController to the directives scope. So you may write your css condition:
form[fieldName].$invalid
instad of:
$parent.myForm[fieldName].$invalid
It is more generic, because you don't need to know the name of your form.
PLUNKR
回答2:
ng-form
directive is linked before the view expressions on child elements are interpolated. This is why you can't have dynamic field names if you want to make use of angular form validation.
But, when creating the form fields dynamically (through ng-repeat
), the fields don't really need to have unique name
attribute. That's because you're allowed to wrap each form element into it's own private ng-form scope:
PLUNKER
app.controller('MainCtrl', function($scope) {
$scope.fields = [
{label: 'First Name', required: false},
{label: 'Last Name', required: false},
{label: 'Email', required: true}
];
});
<body ng-controller="MainCtrl">
<form name="form" class="form-horizontal" ng-submit="save()" novalidate>
<div class="form-group" ng-repeat="field in fields" ng-form="form">
<label class="control-label" ng-class="{required: field.required}">{{field.label}}</label>
<input
type="text"
name="field"
ng-model="field.value"
class="form-control"
ng-required="field.required"
/>
<span
class="help-block error"
ng-show="form.field.$dirty && form.field.$error.required"
>{{field.label}} is required</span>
</div>
<div class="form-group">
<button type="submit" class="btn btn-primary">Save changes</button>
<button type="button" class="btn">Cancel</button>
</div>
</form>
</body>
来源:https://stackoverflow.com/questions/21201087/how-to-set-the-fieldname-using-a-template-in-angularjs