AngularJS 指令 child 通信的问题
Problems with AngularJS directive child communication
一句话问题:
如何在指令中访问 child 指令的控制器?
更长的描述:
我正在编写一些指令来处理来自遥控器(想想电视控制器)的输入。我这样做是因为 HTML 本身没有很好的 focus/cursor 处理。我的问题是我是 AngularJS 的新手,感觉它对我不利。
例如,在特定视图中,我希望能够执行如下操作:
<div>
<my-linear-focus-container direction="horizontal">
<my-grid default-focused="true" style="...">{{gridItems}}</my-grid>
<my-button style="..."></my-button>
</my-linear-focus-container>
</div>
所有想要处理键的视图和 "widgets" 都需要有一个 FocusNode 指令。这些节点将一起创建一个焦点树,并且键将从树中的焦点节点沿着分支传播到根。当新节点获得焦点时,相关树节点(失去焦点、接收焦点等)之间将出现适当的信号。
linearFocusContainer 的职责是在 child widgets/directives 之间切换焦点。所以如果 child A 有焦点(并且不听 right 键)并且用户按下 right linearFocusContainer 将提供焦点child B 紧挨着 child A.
LinearFocusContainer 指令
{
"restrict": "E",
"scope": {},
"template": "<div rs-focus-node keys='keys'></div>",
"link": function (scope) {
$scope.keyListeners = {
left: function () { /* focus child left of current focused */ },
right: function () { /* focus child right of current focused */ },
...
}
$scope.focusEventListeners = {
onFocusReceived: function () { /* focus to default child */ },
...
}
}
}
这是我的问题。为此,我需要访问 FocusNode 指令 "owned by" LinearFocusContainer 指令以便 focus/access 其他 children.
$scope.keyListeners = {
left: function () { focusNode.getChildren()[0].takeFocus(); }
}
这也让我有可能做到:
focusNode.setKeyListeners({
...
});
这样而不是写入作用域中的变量。
您不能直接访问 child 控制器,Angular 不支持此功能。
你可以做的是让 child require
parent 并将 child 控制器传递给 parent 作为:
app.directive('parent', function() {
return {
...
controller: function() {
var childController;
this.setChildController = function(c) {
childController = c;
};
}
};
});
app.directive('child', function() {
return {
...
require: ['parent', 'child'],
controller: function() {
...
},
link: function(scope, elem, attrs, ctrls) {
ctrls[0].setChildController(ctrls[1]);
}
};
});
这里演示原理,如果超过一个children可以相应调整。
解决评论 [...] child 不知道 parent 是什么指令。 [...] 可以要求 "generic types"?
所以不,require
不能接受泛型类型(这将是一个有用的功能,我最近运行经常使用它).我可以建议 2 个解决方案:
多介绍一个"coordinator directive"。例如,对于评论中描述的案例,假设以下 HTML:
<linear-focus-container focus-coordinator>
<my-grid default-focused="true" style="...">{{gridItems}}</my-grid>
</linear-focus-container>
LinearFocusContainer
指令和 myGrid
都需要 focusCoordinator
并通过它进行协作。它甚至可以在 parent 指令的不同类型中实现一些有用的通用功能。
(高度未经测试且可能危险)所有 parent 指令都将 object 与标准 API到 DOM 通过 angular.element.data()
以明确定义的名称。 child 指令在其 DOM parent 的层次结构中向上移动,寻找这个定义明确的名称。至少不要忘记在 $destroy
.
上删除这个 object
为什么不用AngularJS提供的event broadcast/emit
机制
$scope.$broadcast
将在所有子范围内广播一个事件。您可以使用 scope.$on
捕获子范围,类似地通知父范围更改,您可以使用 `$scope.$emit'。
来自父控制器,
$scope.$broadcast('eventName', a_value_or_an_object);
而在子控制器中,
scope.$on('eventName', function($event, value_or_object){});
默认情况下,$emit
将导致事件传播到 $rootScope
,这意味着它将首先到达父级,然后是父级的父范围。
如果你想取消进一步的传播,你可以在事件监听器中使用$event.preventDefault()
。
一句话问题:
如何在指令中访问 child 指令的控制器?
更长的描述:
我正在编写一些指令来处理来自遥控器(想想电视控制器)的输入。我这样做是因为 HTML 本身没有很好的 focus/cursor 处理。我的问题是我是 AngularJS 的新手,感觉它对我不利。
例如,在特定视图中,我希望能够执行如下操作:
<div>
<my-linear-focus-container direction="horizontal">
<my-grid default-focused="true" style="...">{{gridItems}}</my-grid>
<my-button style="..."></my-button>
</my-linear-focus-container>
</div>
所有想要处理键的视图和 "widgets" 都需要有一个 FocusNode 指令。这些节点将一起创建一个焦点树,并且键将从树中的焦点节点沿着分支传播到根。当新节点获得焦点时,相关树节点(失去焦点、接收焦点等)之间将出现适当的信号。
linearFocusContainer 的职责是在 child widgets/directives 之间切换焦点。所以如果 child A 有焦点(并且不听 right 键)并且用户按下 right linearFocusContainer 将提供焦点child B 紧挨着 child A.
LinearFocusContainer 指令
{
"restrict": "E",
"scope": {},
"template": "<div rs-focus-node keys='keys'></div>",
"link": function (scope) {
$scope.keyListeners = {
left: function () { /* focus child left of current focused */ },
right: function () { /* focus child right of current focused */ },
...
}
$scope.focusEventListeners = {
onFocusReceived: function () { /* focus to default child */ },
...
}
}
}
这是我的问题。为此,我需要访问 FocusNode 指令 "owned by" LinearFocusContainer 指令以便 focus/access 其他 children.
$scope.keyListeners = {
left: function () { focusNode.getChildren()[0].takeFocus(); }
}
这也让我有可能做到:
focusNode.setKeyListeners({
...
});
这样而不是写入作用域中的变量。
您不能直接访问 child 控制器,Angular 不支持此功能。
你可以做的是让 child require
parent 并将 child 控制器传递给 parent 作为:
app.directive('parent', function() {
return {
...
controller: function() {
var childController;
this.setChildController = function(c) {
childController = c;
};
}
};
});
app.directive('child', function() {
return {
...
require: ['parent', 'child'],
controller: function() {
...
},
link: function(scope, elem, attrs, ctrls) {
ctrls[0].setChildController(ctrls[1]);
}
};
});
这里演示原理,如果超过一个children可以相应调整。
解决评论 [...] child 不知道 parent 是什么指令。 [...] 可以要求 "generic types"?
所以不,require
不能接受泛型类型(这将是一个有用的功能,我最近运行经常使用它).我可以建议 2 个解决方案:
多介绍一个"coordinator directive"。例如,对于评论中描述的案例,假设以下 HTML:
<linear-focus-container focus-coordinator> <my-grid default-focused="true" style="...">{{gridItems}}</my-grid> </linear-focus-container>
LinearFocusContainer
指令和myGrid
都需要focusCoordinator
并通过它进行协作。它甚至可以在 parent 指令的不同类型中实现一些有用的通用功能。(高度未经测试且可能危险)所有 parent 指令都将 object 与标准 API到 DOM 通过
angular.element.data()
以明确定义的名称。 child 指令在其 DOM parent 的层次结构中向上移动,寻找这个定义明确的名称。至少不要忘记在$destroy
. 上删除这个 object
为什么不用AngularJS提供的event broadcast/emit
机制
$scope.$broadcast
将在所有子范围内广播一个事件。您可以使用 scope.$on
捕获子范围,类似地通知父范围更改,您可以使用 `$scope.$emit'。
来自父控制器,
$scope.$broadcast('eventName', a_value_or_an_object);
而在子控制器中,
scope.$on('eventName', function($event, value_or_object){});
默认情况下,$emit
将导致事件传播到 $rootScope
,这意味着它将首先到达父级,然后是父级的父范围。
如果你想取消进一步的传播,你可以在事件监听器中使用$event.preventDefault()
。