如果以异步方式加载绑定的资源,则函数中的$watch/$timeout包装会中断双向绑定

Wrapping $watch/$timeout in function breaks 2-way binding if bound resource is loaded async

本文关键字:绑定 timeout watch 包装 中断 函数 方式 异步 加载 资源 如果      更新时间:2023-09-26

TL;DR查看示例

我有一个带有独立作用域的指令,其作用域如下所示:{ obj: '=' }。然后我需要将该值应用于模型属性:

link (scope, element, attrs) {
  scope.model = {};
  scope.model.v = scope.obj.value;
}

然后,我需要确保当scope.model.value更改时,父作用域的属性也会更新。所以我做了这样的事情:

scope.$watch('obj.value', function (a,b) {
  if (!angular.equals(a, b)) {
    $timeout(function () {
      scope.model.v = a;
    });
});

反之亦然,对另一个方向具有约束力。

这非常有效,但我想把它放在一个可重用的函数中,这样我就不会总是重复那块代码了。

所以我想出了这个:

/**
 * scope: the scope being altered
 * a:     string representing the first property being bound
 * b:     string representing the second property being bound
 * objEq: boolean to be passed to the scope.$watch object equality argument
 */
var bind = function (scope, a, b, objEq) {
    objEq = typeof objEq === 'undefined' ? false : objEq;
    var getPointer = function (s) {
      var parts = s.split('.');
      var pointer = scope;
      var key = parts.slice(-1);
      for (var i = 0; i < parts.length-1; i++) {
        var part = parts[i];
        pointer = pointer[part];
      }
      return {pointer: pointer, key: key};
    };
    var aObj = getPointer(a);
    var bObj = getPointer(b);
    scope.$watch(a, function (n, o) {
        if (!angular.equals(n, o)) {
            $timeout(function () {
                bObj.pointer[bObj.key] = n;
            });
        }
    }, objEq);
    scope.$watch(b, function (n, o) {
        if (!angular.equals(n, o)) {
            $timeout(function () {
                aObj.pointer[aObj.key] = n;
            });
        }
    }, objEq);
};

如果父作用域的属性被硬编码到控制器中,这将非常有效,但如果该属性是http请求的结果,则会中断。我搞不清楚这里发生了什么。有人能解释一下吗?

下面是一个例子。


此外,仅仅将scope.$watch封装在函数中并不会破坏它

这项工作:

function (scope, a) {
  scope.$watch(a, function (n, o) {
    if (!angular.equals(n, o)) {
      $timeout(function () {
        scope.otherProperty = n;
      });
    }
  });
}

好吧,多亏@PSL对问题的解释,我找到了解决方案。

我只需要调用scope.$watch中的getPointer函数。

scope.$watch(a, function (n, o) {
  if (!angular.equals(n, o)) {
    var bObj = getPointer(b);
    $timeout(function () {
      bObj.pointer[bObj.key] = n;
    });
  }
}, objEq);

示例:jsfiddleplunker