具有共享控制器的Angular指令不共享控制器的$ scope

时间:2015-12-31 00:28:08

标签: angularjs angularjs-directive angularjs-scope

如果我有两个不同的带有隔离范围的指令,并且它们共享一个控制器,那么它们不应该共享控制器的$scope吗?

实际上,我有以下情况:

  1. 我打电话给服务器并传递一些参数,我们称之为xy
  2. 服务器返回一些数据,或者返回null,表示此数据不可用。
  3. 如果返回数据,我需要显示一个按钮;如果用户单击该按钮,它将在单独的div中显示数据。
  4. 如果数据返回,我不会显示该按钮。
  5. 我一直在努力将其作为一组链接指令来实现。我试图这样做,因为这个“组件”将在应用程序中重复使用多次,我试图在两个指令中执行此操作,因为我无法找出任何其他方法来制作一个单指令控制两个不同的元素。

    除了有很多内容之外,还需要按xy值进行关联。因此,如果我有一个特定xy的按钮,则点按该按钮只会切换显示区域的可见性,并显示相同的xy值。有效地,给定视图将具有多个具有不同xy值的视图。

    无论如何,我有一个潜在的工作解决方案,但我似乎遇到了共享范围的问题。当我单击按钮时,我有记录语句,正确显示我们触发“显示”逻辑。但是div的ng-if中的日志记录语句始终将相同的逻辑评估为false并且不显示。

    我的解决方案分为三个部分:

    • 按钮指令
    • “display”指令
    • 由两个
    • 共享的控制器

    我有一个简单的工作示例,我将在下面分享。这篇文章末尾还有一个Plunkr URL。

    这是HTML。中间的<p>标记只是为了证明这两个指令在物理上不相邻。

    <trigger x="apple" y="2" ></trigger>
    <p>Some unrelated dom content...</p>
    <display x="apple" y="2"></display>
    

    这是trigger指令,它是按钮:

    app.directive("trigger", function() {
      return {
        restrict: "E",
        scope: {
          x : "@",
          y : "@"
        },
        transclude: false,
        template: "<button ng-if='hasCalculation(x,y)' ng-click='toggle()'>Trigger x={{x}} & y={{y}}</button>",
        controller: 'testController',
        link: function(scope) {
          scope.doSomeWork();
        }
      };
    });
    

    这是display指令,它应该在按钮切换时显示数据:

    app.directive("display", function() {
      return {
        restrict: 'E',
        scope: {
          x : '@',
          y : '@'
        },
        require: '^trigger',
        transclude: false,
        controller: 'testController',
        template: "<p ng-if='shouldShow(x,y)'>{{getCalculation(x,y)}}</p>"
      };
    });
    

    这是共享控制器testController

    app.controller("testController", ["$scope", function($scope) {
      $scope.shouldShow = [[]];
      $scope.calculatedWork = [[]];
      $scope.doSomeWork = function() {
        var workResult = "We called the server and calculated something asynchonously for x=" + $scope.x + " and y=" + $scope.y;
        if(!$scope.calculatedWork[$scope.x]) {
          $scope.calculatedWork[$scope.x] = [];
        }
        $scope.calculatedWork[$scope.x][$scope.y] = workResult;
      };
    
      $scope.hasCalculation = function(myX, myY) {
        var xRes = $scope.calculatedWork[myX];
        if(!xRes) {
          return false;
        }
        return $scope.calculatedWork[myX][myY]
      }
    
      $scope.toggle = function() {
        if(!$scope.shouldShow[$scope.x]) {
          $scope.shouldShow[$scope.x] = [];
        }
        $scope.shouldShow[$scope.x][$scope.y] = !$scope.shouldShow[$scope.x][$scope.y];
        console.debug("Showing? " + $scope.shouldShow[$scope.x][$scope.y]);
      }
    
      $scope.isVisible = function(myX, myY) {
        console.debug("Checking if we should show for " + myX + " and " + myY);
    
        var willShow;
        if(!$scope.shouldShow[myX]) {
          willShow = false;
        } else {
           willShow = $scope.shouldShow[myX][myY];
        }
        console.debug("Will we show? " + willShow);
        return willShow;
      }
    
      $scope.getCalculation = function(myX, myY) {
        if(!$scope.calculatedWork[myX]) {
          return null;
        }
        return $scope.calculatedWork[myX][myY];
      }
    }]);
    

    这是Plunkr

    如果你去Plunkr,你会看到正确渲染的触发按钮。如果单击该按钮,您将看到toggle()方法正确地将shouldShow的值翻转到与之前相反的值(该方法中有一个控制台调试语句,显示其结果) )。您还会看到isVisible方法的重新评估,该方法确定显示是否应该显示 - 此始终返回false。

    我认为这与我保存相对于$scope的数据和可见性状态这一事实有关。我对此的理解是每个单独的指令都有自己的$scope,但由于它们共享一个控制器,它们不应该共享那个控制器的$scope吗?

1 个答案:

答案 0 :(得分:0)

隔离范围原型继承父范围的属性。

  

在AngularJS中,子范围通常原型继承自其父范围。此规则的一个例外是使用范围的指令:{...} - 这会创建一个&#34; isolate&#34;没有原型继承的范围。(和带有翻译的指令)这个结构通常用于创建一个&#34;可重用的组件&#34;指示。在指令中,默认情况下直接使用父作用域,这意味着您在指令中更改的来自父作用域的任何内容也将在父作用域中更改。如果设置scope:true(而不是scope:{...}),那么原型继承将用于该指令。

来源:https://github.com/angular/angular.js/wiki/Understanding-Scopes

此外,控制器不是单例,它是一个类...因此两个指令不能共享一个控制器。它们每个都实例化控制器。

如果您希望两个控制器共享相同的数据,请使用factory