0

I need a couple of directives performing input field cleanup and validation, just like in this question. All of them are the same except for the cleanup and validation functions themselves and the field name. Currently, I'm copying it like

angular.module('myModule')

.directive('validateFoo', function() {
      return {
        restrict: 'A',
        require: 'ngModel',
        link: function($scope, element, attrs, ngModel) {
            // THESE THREE LINES SHOULD BE ARGUMENTS
            var isValid = isValidFoo;
            var clean = cleanFoo;
            var name = "foo";

            element = $(element);

            var cleanAndValidate = function(x) {
                var y = clean(x);
                var ok = isValid(y);
                ngModel.$setValidity(name, ok);
                return y;
            };

            ngModel.$parsers.push(cleanAndValidate);

            var fix = function() {
                var x = element.val();
                var y = clean(x);
                if (x===y) return y;
                var e = element[0];
                var start = e.selectionStart;
                var end = e.selectionEnd;
                element.val(y);
                var delta = y.length - x.length;
                e.setSelectionRange(start + delta, end + delta);
                return y;
            };

            element.keyup(function() {
                fix();
            });
        }
    };
})

which is obviously a bad idea. I guess I should be able to do it using a closure, but I'd also like to preserve the overall structure (all my files start with angular.module followed by a definition). If I had access to the directive name in the body, I could get the three variables from their defining object.

Community
  • 1
  • 1
maaartinus
  • 44,714
  • 32
  • 161
  • 320
  • Unrelated to the question, but why are you wrapping element with $()? First of all, it is already wrapped by angular, and if you have jquery loaded then you have all of its functions in the angular element wrapper already. Second, it's bad practice to override function parameters, what if you want to refer to the original element that the function received? – haimlit May 10 '14 at 17:07
  • @haimlit: I think I need it to be wrapped by jQuery. But I'm loading jQuery first, so I can remove it. Concerning the "bad" practice of overriding function parameters: I disagree. I do this on purpose whenever I know I won't need the original as it prevents using the original by mistake. – maaartinus May 10 '14 at 19:08
  • I stand corrected. I thought jshint has a rule about it, but it was my imagination, so it's not bad practice, just a question of flavor. Sorry! – haimlit May 10 '14 at 19:33
  • @haimlit: No need to say sorry, I'm happy to get comments. I've never seen anyone advocating overriding arguments, it's just that I got too often burned by mistakenly using the original. – maaartinus May 10 '14 at 20:14

1 Answers1

2

All of them are the same except for the cleanup and validation functions themselves and the field name

I think you need to add a scope to your custom directive; then you can pass in the functions and field that need to be processed. Something like this:

.directive('validateFoo', function() {
      return {
        restrict: 'A',
        require: 'ngModel',
        scope : { 
            // DEFINE These Arguments in the scope
            isvalid : "=isvalid",
            clean : "=clean",
            name : "=name"
        }
        link: function($scope, element, attrs, ngModel) {

            element = $(element);

            // modify this method to access your clean/isvalid/name values in the $scope
            var cleanAndValidate = function(x) {
                var y = $scope.clean(x);
                var ok = $scope.isValid(y);
                ngModel.$setValidity($scope.name, ok);
                LOG name, x, y, ok
                return y;
            };

            ngModel.$parsers.push(cleanAndValidate);

            var fix = function() {
                var x = element.val();
                var y = clean(x);
                if (x===y) return y;
                var e = element[0];
                var start = e.selectionStart;
                var end = e.selectionEnd;
                element.val(y);
                var delta = y.length - x.length;
                e.setSelectionRange(start + delta, end + delta);
                return y;
            };

            element.keyup(function() {
                fix();
            });
        }
    };
})

When you use the directive, you can pass in the function and values, sort of like this:

<validate-foo isvalid="isValidFoo" clean="cleanfoo" name="foo" />
JeffryHouser
  • 39,401
  • 4
  • 38
  • 59
  • I guess, that's exactly what I wanted. But somehow it doesn't work, I get no arguments passed (`name` etc. are undefined). Actually nothing interesting shows in the `scope`. Any idea why this could be? – maaartinus May 10 '14 at 19:15
  • 1
    I know it know... the variables are not in the scope on the call site (and they shouldn't be). I need just their names, so `@` instead of `=` works for me. – maaartinus May 10 '14 at 19:28