Search code examples
angularjsangular-directive

Compile directive from another directive


I'm currently adding the ng-model-options directive to a number of my input boxes to debounce. My elements look like this:

<input type="text" ng-model="search" ng-model-options="{ debounce: { 'default': 200 } }" />

I would like to put this in a directive so:

  1. My markup is less cumbersome.
  2. I can control the debounce value in one place in case I want to change it.

I ultimately want the markup to look like this where it uses a debounce directive:

<input type="text" ng-model="search" debounce />

I've attempted to implement this directive like so:

app.directive('debounce', ['$compile', function ($compile) {
    return {
        restrict: 'A',
        replace: false,
        link: function (scope, element, attrs) {
            element.attr('ng-model-options', "{ debounce: { 'default': 200 } }");
            $compile(element.contents())(scope);
        }
    }
}]);

It appears to result in the correct HTML, but the debounce is not doing anything. What is wrong with my directive?


Solution

  • Actually you dont need to even access the element. you can set the options in the ngModel controller's $options property and set the necessary values like this:

    ctrl.$options = {debounce:{default:300}, updateOnDefault: true};
    

    Code:

    .directive('debounce', ['$timeout',
      function($timeout) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function(scope, element, attrs, ctrl) {
            var options = ctrl.$options || {};
    
            ctrl.$options = angular.extend(options, {
              debounce: {
                default: 300
              },
              updateOnDefault: true
            });
    
          }
        }
      }
    ]);
    

    angular.module('app', []).directive('debounce', ['$timeout',
      function($timeout) {
        return {
          restrict: 'A',
    
          require: 'ngModel',
          replace: false,
    
          link: function(scope, element, attrs, ctrl) {
            var options = ctrl.$options || {};
            
            ctrl.$options = angular.extend(options || {}, {
              debounce: {
                default: 300
              },
              updateOnDefault: true
            });
            
           
          }
        }
      }
    ]).controller('test', function() {
      this.callMe = function() {
        console.log(this.search);
      }
    });
    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.4.0/angular.min.js"></script>
    <div ng-app="app" ng-controller="test as vm">
    
      <input type="text" ng-model="vm.search" debounce ng-change="vm.callMe()" />
      <input type="text" ng-model="vm.search" ng-change="vm.callMe()" ng-model-options="{ debounce: { 'default': 200 } }" />{{vm.search}}
    </div>

    If you want to make it more configurable by accepting a debounce value as attribute then:

    .directive('debounce', ['$timeout',
      function($timeout) {
        return {
          restrict: 'A',
          require: 'ngModel',
          link: function(scope, element, attrs, ctrl) {
            var options = ctrl.$options || {updateOnDefault: true};
    
            ctrl.$options = angular.extend(options, {
              debounce: {
                default:  +attrs.debounce || 300
              }
            });
    
          }
        }
      }
    ])