Директива компиляции из другой директивы

В настоящее время я добавляю директиву ng-model-options в несколько полей ввода для устранения проблемы. Мои элементы выглядят так:

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

Я хотел бы поместить это в директиву так:

  1. Моя разметка менее громоздкая.
  2. Я могу контролировать значение противодействия в одном месте на случай, если я захочу его изменить.

В конечном итоге я хочу, чтобы разметка выглядела так, где используется директива debounce:

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

Я попытался реализовать эту директиву так:

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);
        }
    }
}]);

Похоже, что в результате получился правильный HTML, но отладчик ничего не делает. Что не так с моей директивой?


person im1dermike    schedule 29.10.2015    source источник
comment
@PankajParkar: Это не работает ...   -  person im1dermike    schedule 29.10.2015


Ответы (3)


На самом деле вам даже не нужно обращаться к элементу. вы можете установить параметры в свойстве $options контроллера ngModel и установить необходимые значения следующим образом:

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

Код:

.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>

Если вы хотите сделать его более настраиваемым, приняв значение debounce в качестве атрибута, тогда:

.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
          }
        });

      }
    }
  }
])
person PSL    schedule 29.10.2015
comment
Это действительно милый ответ .. +1 - person Pankaj Parkar; 29.10.2015

Динамическое добавление директив самому себе не очень хорошо поддерживается в Angular. Вы на правильном пути с компиляцией, однако директива ng-model уже была скомпилирована в тот момент, когда вы это делаете.

Поскольку для ngModel задокументировано выполнение с приоритетом 1, вам необходимо одновременно иметь terminal и запускать как минимум с приоритетом 2, чтобы ваш ng-model-options был связан в надлежащее время.

app.directive('debounce', ['$compile', function ($compile) {
  return {
    restrict: 'A',
    priority: 2,
    terminal: true,
    compile: function(tElement) {
      tElement.attr('ng-model-options', "{ debounce: { 'default': 200 } }");

      return function (scope, element, attrs, controllers, transclude) {
        $compile(element, null, 2)(scope, {
          parentBoundTranscludeFn: transclude
        });
      };
    } 
  }
}]);
person dherman    schedule 29.10.2015

Чтобы сделать то же самое, вам нужно добавить директиву с более высоким значением priority, чтобы избежать компиляции другой директивы &, для которой параметр terminal будет установлен в значение true. Это будет означать, что никакая другая директива, когда эта директива не запущена, затем из этой директивы remove атрибут директивы и добавление ng-model-options для применения изменения debounce.

Удаление атрибута debounce необходимо, чтобы избежать бесконечной компиляции

Директива

app.directive('debounce', ['$compile', function ($compile) {
  return {
    restrict: 'A',
    priority: 1,
    terminal: true, 
    compile: function(element, attrs) {
      //compile when scope is not linked to the DOM.
      element.attr('ng-model-options', "{ debounce: { 'default': 200 } }");
      element.removeAttr('debounce'); //this removal is necessary to avoid infinite compile
      var compile = $compile(element);
      return function (scope, element, attrs) {
        var link = compile(scope);
      };
    } 
  }
}]);
person Pankaj Parkar    schedule 29.10.2015
comment
Удаление debounce не требуется, если вы ограничиваете максимальный приоритет того, что связано, как в моем ответе. Вы должны сделать это в любом случае, чтобы избежать многократного связывания директив с более высоким приоритетом. Это также не удастся, если директивы с более низким приоритетом используют включение. - person dherman; 29.10.2015
comment
@dherman, но упоминание директивы с более высоким приоритетом не позволяет другой директиве делать включение. - person Pankaj Parkar; 29.10.2015