如何在源范围AngularJS的上下文中从指令中观察模型

时间:2015-03-24 08:47:08

标签: javascript angularjs

我需要在指令中观察模型。

angular.module('app', [])
.directive('myDirective', [function() {
    return {
       restrict: 'A',
       scope: {
          modelToWatch: '@'
       },
       link: function(scope, element, attrs) {
           scope.$watch(scope.modelToWatch, function(val) {
              // do something...
           });
       }
    };
]})

.controller('MyController', ['$scope', function($scope) {
    $scope.obj = {
       foo: 'val'
    };
}]);

<div ng-controller="MyController">
    <div my-directive model-to-watch="obj.foo"></div>        
</div>

以上工作正常。

但是,当模型的实际所有者与指令之间存在中间范围时,我遇到了问题。

我使用另一个控制器来演示以下场景:

.controller('AnotherController', ['$scope', function($scope) {}])

<div ng-controller="MyController">
    <div ng-controller="AnotherController">
        <div my-directive model-to-watch="obj.foo"></div>
    </div>
</div>

在上面的情况下,我可以使用下面的代码查找$ parent树以查找拥有我想要观看的属性的范围:

...

link: function(scope, element, attrs) {
   var contextScope = scope;

   // find for the scope which owns the property that we want to watch
   while (contextScope != null && contextScope.hasOwnProperty(attrs.modelToWatch)) {
       contextScope = contextScope.$parent;
   }

   // use the scope found to watch the model
   if (contextScope != null) {
      contextScope.$watch(scope.modelToWatch, function(val) {
          // do something...
      });
   }
}

另外一个问题是,如果modelToWatch是一个复杂的表达式(例如:“tableParams.filter(。。shop_id”),则无法依赖hasOwnProperty。

是否有一种简单的方法可以在其所有者范围内观察模型?或者甚至可以从原型儿童那里观看模型?

或者我可以将范围作为参数传递,所以至少我不需要寻找它......

restrict: 'A',
scope: {
    modelToWatch: '@',
    sourceScope: '=', // don't know how to do this..
}

注意:我需要使用隔离范围

正如@pixelbit建议的那样,我尝试使用$ eval来找到正确的范围

link: function(scope, element, attrs) {
   var contextScope = scope;

   // find for the scope which owns the property that we want to watch
   while (contextScope != null && contextScope.$eval(attrs.modelToWatch) != undefined) {
       contextScope = contextScope.$parent;
   }

   ...
}

适用于大多数情况,除非当modelToWatch表达式实际评估为未定义时。当前范围中不存在modelToWatch(意味着它不是所有者)或者modelToWatch表达式恰好评估为undefined时存在歧义

3 个答案:

答案 0 :(得分:0)

不需要隔离范围 - 您可以继承范围。另外,为了解决复杂的表达式,可以使用scope。$ eval来评估模型并找到合适的范围。一旦您对模型进行了评估,请从观察的函数中返回它:

angular.module('app', [])
.directive('myDirective', [function() {
    return {
       restrict: 'A',
       scope: false,
       link: function(scope, element, attrs) {
           scope.$watch(function() {
               return scope.$eval(attrs.modelToWatch);
           }, function(val) {
              // do something...
           });
       }
    };
]})

如果你必须使用隔离范围,那么观察一个函数并返回模型:

angular.module('app', [])
.directive('myDirective', [function() {
    return {
       restrict: 'A',
       scope: {
           modelToWatch: '='
       },
       link: function(scope, element, attrs) {
           scope.$watch(function() {
               return scope.modelToWatch;
           }, function(val) {
              // do something...
           });
       }
    };
]})

答案 1 :(得分:0)

您可以直接在指令中声明控制器:

angular.module('app', [])
.directive('myDirective', [function() {
  return {
   restrict: 'A',
   scope: {
      modelToWatch: '='
   },
   link: function(scope, element, attrs) {
       scope.$watch(scope.modelToWatch, function(val) {
          // do something...
       });
   },
   controller: 'MyController'
  };
]})

.controller('MyController', ['$scope', function($scope) {
  $scope.obj = {
    foo: 'val'
  };


}]);

<div my-directive model-to-watch="obj.foo"></div>     

这样,当你调用你的指令时,你的控制器将首先被实例化,然后链接将被执行,共享相同的范围。

答案 2 :(得分:0)

您可以改为观看功能:

scope.$watch(function() { 
        return scope.modelToWatch; 
    }, function(val) {
        // do something
});