假设我们有一些嵌套指令:
<big-poppa>
<baby-bird></baby-bird>
</big-poppa>
让我们说big-poppa
想要创建一个他所有的孩子指令可以共享的组件。将它放在控制器中会很好,但是这个组件需要DOM,所以它需要在链接函数中构建。
然后让我们说baby-bird
组件想要从组件中读取。也许它想要听取它的事件,或许发送一两个命令。挑战是控制器触发dom(第一个父节点,然后是子节点),后链接方法触发另一个方向,因此执行顺序如下所示:
父母的link
方法在孩子之后触发的事实是导致我的内部指令通信挑战的原因。我希望父级构建共享DOM组件,但DOM构造应该在链接函数中进行。因此父母在任何孩子之后构建组件
我可以用超时(粗略)或承诺(复杂/非惯用?)来解决这个问题。这是小提琴:
var app = angular.module('app',[]);
app.directive('bigPoppa', function($q){
return {
restrict: 'E',
controller: function($scope){
console.log('bigPoppa controller');
var d = $q.defer()
$scope.bigPoppaLinkDeferred = d
$scope.bigPoppaLink = d.promise
},
link: function(scope, el, attrs){
console.log('bigPoppa link');
scope.componentThatNeedsDom = { el: el, title: 'Something' };
scope.bigPoppaLinkDeferred.resolve()
}
}
});
app.directive('babyBird', function(){
return {
restrict: 'E',
controller: function(){ console.log('babyBird controller'); },
link: function(scope, el, attrs, bigPoppaController){
console.log('babyBird link');
// console.log('poppa DOM component', scope.componentThatNeedsDom); // Not yet defined, because the parent's link function runs after the child's
// setTimeout(function(){ console.log('poppa DOM component', scope.componentThatNeedsDom); }, 1); // Works, but gross
scope.bigPoppaLink.then(function(){
console.log('poppa DOM component', scope.componentThatNeedsDom);
}); // works, but so complex!
}
}
});
console.log(''); // blank line
这里有很多背景,但我的问题很简单:
在父指令运行其后链接功能后,是否有一种干净的方式在子指令中执行行为?
也许使用priority
或pre
和post
链接方法?
答案 0 :(得分:1)
实现此目的的另一种方法是使用普通的Angular范围事件从父链接函数传递给子元素。
var app = angular.module('app',[]);
app.directive('bigPoppa', function($q){
return {
restrict: 'E',
link: function(scope, el, attrs){
scope.$broadcast('bigPoppa::initialised', {el: el, title: 'Something'});
}
}
});
app.directive('babyBird', function(){
return {
restrict: 'E',
link: function(scope, el, attrs) {
scope.$on('bigPoppa::initialised', function(e, componentThatNeedsDom) {
console.log('poppa DOM component in bird linking function', componentThatNeedsDom);
});
}
}
});
可以看到http://jsfiddle.net/michalcharemza/kerptcrw/3/
这种方式有以下好处:
答案 1 :(得分:0)
根据实验,如果我错了要求更正,我发现Angular按以下顺序运行其编译阶段:
1. compile methods of all directives both parent and child, run in flat order
2. parent controller
3. parent pre-link
4. (all actions of children directives)
5. parent post-link (AKA regular `link` function)
此实验的公开要点是:https://gist.github.com/SimpleAsCouldBe/4197b03424bd7766cc62
有了这些知识,似乎父指令的pre-link
回调非常合适。解决方案如下所示:
var app = angular.module('app',[]);
app.directive('bigPoppa', function($q){
return {
restrict: 'E',
compile: function(scope, el) {
return {
pre: function(scope, el) {
console.log('bigPoppa pre');
scope.componentThatNeedsDom = { el: el, title: 'Something' };
}
};
}
}
});
app.directive('babyBird', function(){
return {
restrict: 'E',
link: function(scope, el, attrs, bigPoppaController){
console.log('babyBird post-link');
console.log('bigPoppa DOM-dependent component', scope.componentThatNeedsDom);
}
}
});
感谢@runTarm和this question将我指向pre-link
方向。
答案 2 :(得分:0)
您可以使用两种模式来实现您想要的效果
您可以在子链接函数中使用代码来响应父指令控制器中的更改,通过require
父指令的控制器,并在某个值上创建$watch
它
如果您需要在父链接函数中运行某些内容,然后只更改其控制器中的值,则指令可以require
本身,并且可以访问来自链接功能的控制器。
将这些放在你的例子中变成:
var app = angular.module('app',[]);
app.directive('bigPoppa', function($q){
return {
restrict: 'E',
require: 'bigPoppa',
controller: function($scope) {
this.componentThatNeedsDom = null;
},
link: function(scope, el, attrs, controller){
controller.componentThatNeedsDom = { el: el, title: 'Something' };
}
}
});
app.directive('babyBird', function(){
return {
restrict: 'E',
require: '^bigPoppa',
link: function(scope, el, attrs, bigPoppaController){
scope.$watch(function() {
return bigPoppaController.componentThatNeedsDom
}, function(componentThatNeedsDom) {
console.log('poppa DOM component in bird linking function', componentThatNeedsDom);
});
}
}
});
可以在http://jsfiddle.net/4L5bj/1/看到。这样做的好处就在于它不依赖于范围继承,并且不会使用仅由这些指令使用的值来污染范围。