从指令更新控制器范围

Update controller scope from directive

我正在使用 AngularJS 指令创建可重用的 UI 组件。我想要一个包含我的业务逻辑和嵌套组件(指令)的控制器。我希望指令能够在控制器范围内操作单个 属性。这些指令需要有一个独立的范围,因为我可能会多次使用同一个指令,并且每个实例都需要绑定到一个特定的控制器范围 属性.

到目前为止,我可以将更改应用回控制器范围的唯一方法是从指令中调用 scope.$apply()。但是,由于 rootScope:inprog(范围操作正在进行)错误,当我在 ng-click 回调中时,这会中断。

所以我的问题:当子指令更新了控制器范围内的值时,让我的控制器知道的最佳方法是什么?

我考虑过在控制器上有一个函数,指令可以调用它来进行更新,但这对我来说似乎很重。

这是我在 ng-click 回调时中断的代码。 请记住,我不只是想解决 ng-click 问题。我想要最好的整体解决方案来应用可重用指令来修改父级 scope/model。

html

<div ng-controller="myCtrl">
    <my-directive value="val1"></my-directive>
</div>

控制器

...
.controller('myCtrl', ['$scope', function ($scope) {
    $scope.val1 = 'something';
}});

指令

...
.directive('myDirective', [function () {

return {
    link: function(scope) {
        scope.buttonClick = function () {
            var val = 'new value';
            scope.value = val;
            scope.$apply(); 
        };
    },
    scope: {
        value: '='
    },
    template: '<button ng-click="buttonClick()"></button>'
};
}]);

指令中双向数据绑定的目的正是您要问的——“[允许]指令修改父项scope/model”。

首先,仔细检查您是否在指令属性上正确设置了双向数据绑定,该指令属性公开了您要在范围之间共享的变量。在控制器中,如果您需要在值更改时执行某些操作,则可以使用 $watch 来检测更新。此外,您还可以选择向指令添加事件处理程序属性。这允许指令在发生某些事情时调用函数。这是一个例子:

<div ng-controller="myCtrl">
    <my-directive value="val1" on-val-change="myFunc"> <!-- Added on-change binding -->
        <button ng-click="buttonClick()"></button>
    </my-directive>
</div>

我认为你关于 $scope.apply 的问题是转移注意力。我不确定在您开发此演示和问题时它为您解决了什么问题,但这不是它的目的,FWIW your example works for me without it.

您不必担心这个问题 ("make controller aware ... that [something] modified a value on a scope"); Angular 的数据绑定会自动处理。

这里有点复杂,因为使用指令,需要考虑多个范围。外部作用域属于 <div ng-controller=myCtrl>,该作用域有一个 .val 属性,还有一个由 <my-directive> 创建的内部作用域,它也有一个 .val 属性,并且 myDirective 中的 buttonClick 处理程序修改了内部处理程序。但是您使用 value: '=' 声明了 myDirective 的范围,它设置了内部和外部范围之间 属性 值的双向同步。

所以它应该会自动工作,在我根据你的问题代码创建的 plunker 中,它确实会自动工作。

那么 scope.$apply 是从哪里来的呢?它明确用于在 Angular 不知道需要时触发摘要循环。 (如果你在 Angular 知道它已经需要一个摘要循环时使用它,你会得到一个嵌套的摘要循环和你注意到的 "inprog" 错误。)Here's the doc link,我引用“ $apply() 用于从 angular 框架之外执行 angular 中的表达式”。您需要使用它,例如,在响应使用非 Angular 方法设置的事件处理程序时——直接 DOM 事件绑定、jQuery、socket.io 等. 如果您在 Angular 应用程序中使用这些机制,通常最好将它们包装在处理 Angular-to-non-Angular 接口的指令或服务中,以便其余的您的应用不必担心。

(scope.$apply 实际上是 scope.$digest 的包装器,它还管理异常处理。这在文档中不是很清楚。我发现它更容易理解 name/behavior $digest,然后认为$apply为"the friendlier version of $digest that I'm actually supposed to use"。)

关于 $apply 的最后一个说明;它需要一个函数回调参数,你应该在这个回调中完成工作。如果你做了一些工作然后调用 $apply 之后没有参数,它会起作用,但在那一点上它与 $digest 相同。所以如果你确实需要在这里使用$apply,它应该看起来更像:

scope.buttonClick = function() { scope.$apply(function() { scope.value = newValue; }); });