16

I would like to create a custom input type similar to the way AngularJS implements "email", for example.

<input type="email" ng-model="user.email" />

What I would like to create is an input type like this:

<input type="path" ng-model="page.path" />

Any ideas on how this can be accomplished? So far, I've only been able to figure out how to implement custom directives where 'path' is the name of the tag, attribute or class.

For example, I can get this to work but it is inconsistent with the other form fields and I'd really like them to look the same.

<input type="text" ng-model="page.path" path />
app.directive('path', function() {
  return {
    require: 'ngModel',
    link: function(scope, elm, attrs, ctrl) { ... }
  };
});
Partha Sarathi Ghosh
  • 10,936
  • 20
  • 59
  • 84
JamesOR
  • 1,138
  • 1
  • 6
  • 15

3 Answers3

18

You can create your own input type="path" by creating an input directive with custom logic if the type attribute is set to "path".

I've created a simple example that simply replaces \ with /. The directive looks like this:

module.directive('input', function() {
    return {
        restrict: 'E',
        require: 'ngModel',
        link: function (scope, element, attr, ngModel) {
          if (attr.type !== 'path') return;

          // Override the input event and add custom 'path' logic
          element.unbind('input');
          element.bind('input', function () {
            var path = this.value.replace(/\\/g, '/');

            scope.$apply(function () {
              ngModel.$setViewValue(path);
            });
          });
        }
    };
});

Example

Update: Changed on, off to bind, unbind to remove jQuery dependency. Example updated.

Martin
  • 8,876
  • 2
  • 35
  • 36
  • 1
    This creates an error for type="file", because angular is expecting ngmodel now – Pascalius Jul 29 '13 at 17:43
  • 1
    @Pascalius you can change the require line to: `require: '?ngModel'` no make it optional. – Martin Aug 03 '13 at 11:04
  • `off` and `on` are jQuery methods. This won't work unless you also have jQuery loaded. – Amy B Aug 21 '13 at 09:58
  • Thanks @AmyB. No need to have a jQuery dependency on this simple directive. I updated the code and example. – Martin Aug 22 '13 at 14:32
  • @AmyB - It depends which version of Angular you are using and it's included version of jQLite, in 1.2.x they have updated it to have on and off. See here http://docs.angularjs.org/api/angular.element – Simeon Cheeseman Nov 12 '13 at 03:55
  • Considering that I have created a Password type and I want this password to follow my security rules like have more than 8 characters. How could I send an invalid response to user? – Luis Felipe Perez Jun 17 '15 at 22:35
  • Is there a way to do exactly that in vuejs as well? – user470370 Oct 20 '18 at 14:42
2

An alternative solution can be achieved by using the $parsers property of the ngModelController. This property represents a chain of parsers that are applied to the value of the input component before passing them to validation (and eventually assigning them to the model). With this, the solution can be written as:

module.directive('input', function() {
    return {
        restrict: 'E',
        require: 'ngModel',
        link: function (scope, element, attr, ngModel) {
          if (attr.type !== 'path') return;

          ngModel.$parsers.push(function(v) {
            return v.replace(/\\/g, '/');
          });
        }
    };
});

Note that there is another property $formatters which is a pipeline of formatters that transform a model value into the value displayed in the input.

See here for the plunker.

widmoser
  • 146
  • 1
  • 3
0

Considering compile function is the first in line, would it not be better with:

module.directive('input', function() {
  return {
    restrict: 'E',
    require: 'ngModel',
    compile: function Compile(tElement, tAttrs) {
      if (tAttrs.type !== 'path') return;

      return function PostLink(scope, element, attr, ngModel) {
        // Override the input event and add custom 'path' logic
        element.unbind('input');
        element.bind('input', function () {
          var path = this.value.replace(/\\/g, '/');

          scope.$apply(function () {
            ngModel.$setViewValue(path);
          });
        });
      }
    }
  };
});
Hotkiz
  • 113
  • 2
  • 9