在指令中调用$setViewValue并不会触发控制器中的$watch

Calling $setViewValue in a directive doesn't fire $watch in the controller

本文关键字:控制器 watch 指令 调用 setViewValue      更新时间:2023-09-26

我正在使用一个指令与ngModelController像这样

  var directive = {
    link: link,
    restrict: 'E',
    require: '?ngModel'
  }

我的链接函数是这样做的(我省略了长指令的东西)

function link(scope, element, attrs, ctrl) {
  scope.updateModel = function(){
    var newModel = {};
    newModel.aValue = 'foo';
    ctrl.$setViewValue(newModel);
  }
}

我在控制器

中使用指令

View:这是正确更新的

<my-directive ng-model="aModel">
  {{aModel.aValue}} // This is changed correctly when I update the value in the directive
</my-directive>

控制器:这里是我有问题的地方

$scope.aModel = {};
$scope.aModel.aValue = 'bar';
$scope.$watch('aModel', function(newValue, oldValue){
    console.log('aModel changed'); // This is never fired
});
$scope.$watch('aModel.aValue', function(newValue, oldValue){
    console.log('aModel changed'); // This is neither fired
});

$setViewValue的文档说

"注意,调用这个函数不会触发$digest。"

所以我尝试用$render, $digest$apply手动启动它,但每次都遇到麻烦,因为控制台说$digest is already in progress .
那么问题是什么呢?

我刚意识到一件很有趣的事。
添加$timeout来检查控制器中的值,我意识到它实际上并没有改变,这可能就是没有调用$watch函数的原因。
但由于视图是更新的,我不太明白。

情况如下:

控制器

$timeout(function(){
  console.log('Check aModel value in the controller');
  console.log($scope.aModel.aValue); // This is always 'bar' even if the view display 'foo'
},10000);

更新2

我想我发现了问题:ngModel不能是一个对象,但它必须是一个值。
我留下这个问题,看看是否有人提出不同的解决方案,但我想这就是问题的关键。

更新3
我错了,你可以把ngModel更新为对象,但是没有办法触发$watch方法。
我创建了一个活塞来显示问题。
我设置了一个较大的超时以避免任何$digest正在进行的问题。
如有任何提示,我将不胜感激。

$watch函数添加第三个参数true

使用不带时间参数的$timeout来调用$apply并触发摘要循环。这将导致它延迟几分之一秒执行,并且不会在摘要循环错误中运行到摘要循环中。显然,你所做的不是"angular感知",所以你需要使用$apply来触发摘要循环,但是你在一个摘要循环中,所以你需要使用上面提到的"hack"。