ade b
ade b

Reputation: 45

Cannot bind angular validation error using directive isolate scope

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

Answers (1)

Michael Kang
Michael Kang

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

Related Questions