从另一个指令编译指令
Compile directive from another directive
我目前正在将 ng-model-options
指令添加到我的一些输入框以进行去抖动。我的元素如下所示:
<input type="text" ng-model="search" ng-model-options="{ debounce: { 'default': 200 } }" />
我想把它放在一个指令中:
- 我的标记不那么繁琐。
- 我可以在一个地方控制去抖动值,以备不时之需。
我最终希望标记在使用 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,但去抖没有做任何事情。我的指令有什么问题?
Angular 不支持动态地向自己添加指令。您在编译方面走在正确的轨道上,但是 ng-model
指令已经在您执行此操作时进行了编译。
由于 ngModel
被记录为优先级 1 的 运行,您至少需要同时处于优先级 2 的 terminal
和 运行 以便您的 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
});
};
}
}
}]);
要执行相同的操作,您需要添加具有更高 priority
的指令以避免编译其他指令,这会将 terminal
选项设置为 true。这将表明当该指令为 运行 时没有其他指令,然后从该指令 remove
指令属性并添加 ng-model-options
以应用去抖动更改。
Removal of debounce
attribute is necessary to avoid infinite compile
指令
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);
};
}
}
}]);
实际上您甚至不需要访问该元素。您可以在 ngModel 控制器的 $options
属性 中设置选项并设置必要的值,如下所示:
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>
如果您想通过接受去抖动值作为属性来使其更具可配置性,那么:
.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
}
});
}
}
}
])
我目前正在将 ng-model-options
指令添加到我的一些输入框以进行去抖动。我的元素如下所示:
<input type="text" ng-model="search" ng-model-options="{ debounce: { 'default': 200 } }" />
我想把它放在一个指令中:
- 我的标记不那么繁琐。
- 我可以在一个地方控制去抖动值,以备不时之需。
我最终希望标记在使用 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,但去抖没有做任何事情。我的指令有什么问题?
Angular 不支持动态地向自己添加指令。您在编译方面走在正确的轨道上,但是 ng-model
指令已经在您执行此操作时进行了编译。
由于 ngModel
被记录为优先级 1 的 运行,您至少需要同时处于优先级 2 的 terminal
和 运行 以便您的 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
});
};
}
}
}]);
要执行相同的操作,您需要添加具有更高 priority
的指令以避免编译其他指令,这会将 terminal
选项设置为 true。这将表明当该指令为 运行 时没有其他指令,然后从该指令 remove
指令属性并添加 ng-model-options
以应用去抖动更改。
Removal of
debounce
attribute is necessary to avoid infinite compile
指令
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);
};
}
}
}]);
实际上您甚至不需要访问该元素。您可以在 ngModel 控制器的 $options
属性 中设置选项并设置必要的值,如下所示:
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>
如果您想通过接受去抖动值作为属性来使其更具可配置性,那么:
.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
}
});
}
}
}
])