Reputation: 45
My overall aim is to componentize all our re-usable widgets as angular directives but am struggling when trying to make an angular form error message directive. I have read numerous posts but couldn't see how to achieve this.
All my directives have isolate scope.
My main issue is that I do not know how to get the pattern attribute to bind correctly to the ng-show attribute of the at-error-message directives span element so that the error message dynamically hides\shows based on the pattern.
The outer HTML is:
<body ng-controller="atVrmLookup">
<ng-form name="vrmForm" novalidate>
<at-input name="registration" label="Registration" required model="vrmLookup.registration" minlength="3"></at-input>
<at-error-message pattern="vrmForm.registration.$error.required" message="Please enter a registration"></at-error-message>
</ng-form>
</body>
The atInput directive is
uiComponents.directive('atInput', function () {
return {
// use an inline template for increased
template: '<div><label>{{label}}</label><div><input name="{{name}}" type="text" ng-model="model" ng-minlength="{{minlength}}"/></div></div>',
// restrict directive matching to elements
restrict: 'E',
scope: {
name: '@',
label: '@',
minlength: '@',
model:'=model'
},
compile: function (element, attr, scope) {
var input = element.find('input');
if (!_.isUndefined(attr.required)) {
input.attr("required", "true");
}
},
controller: function ($scope, $element, $attrs) {
// declare some default values
}
};
});
the atErrorMessageDirective is
uiComponents.directive('atErrorMessage', function () {
return {
// use an inline template for increased
template: '<span class="error" ng-show="pattern">{{message}}</span>',
// restrict directive matching to elements
restrict: 'E',
scope: {
message: '@',
pattern: '='
},
controller: function ($scope, $element, $attrs) {
// declare some default values
}
};
});
Here is plunkr to demonstrate the issue.
http://plnkr.co/edit/5tdsqSXg0y5bfQqJARFB
Any help would be appreciated.
Upvotes: 1
Views: 1365
Reputation: 52847
The input name cannot be an angular binding expression. Instead, use a template function, and build your template string:
template: function($element, $attr) {
return '<div><label>{{label}}</label><div>' +
'<input name="' + $attr.name + '" type="text" ng-model="model" ng-minlength="{{minlength}}"/>' +
'</div></div>';
}
Alternate Solution
Implement a dynamic-name directive, and use the API exposed by the angular form directive to programmatically set the name of the ngModel, and add the ngModel control to the form:
.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]);
}
};
}])
Then in your template:
template: '<div><label>{{label}}</label><div><input dynamic-name="{{name}}" type="text" ng-model="model" ng-minlength="{{minlength}}"/></div></div>',
Note: This solution works because fortunately, ngForm provides programmatic access to customize its behavior. If ngForm's controller had not exposed an API, then you might have been SOL. It's good practice to think of the API you expose from your own custom directive's controllers - you never know how it can be used by other directives.
Upvotes: 1