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 个解决方案:

  1. 多介绍一个"coordinator directive"。例如,对于评论中描述的案例,假设以下 HTML:

    <linear-focus-container focus-coordinator>
        <my-grid default-focused="true" style="...">{{gridItems}}</my-grid>
    </linear-focus-container>
    

    LinearFocusContainer 指令和 myGrid 都需要 focusCoordinator 并通过它进行协作。它甚至可以在 parent 指令的不同类型中实现一些有用的通用功能。

  2. 高度未经测试且可能危险)所有 parent 指令都将 object 与标准 API到 DOM 通过 angular.element.data() 以明确定义的名称。 child 指令在其 DOM parent 的层次结构中向上移动,寻找这个定义明确的名称。至少不要忘记在 $destroy.

  3. 上删除这个 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()