一个元素的多个指令可以共享一个隔离的范围吗?

时间:2014-04-22 13:36:45

标签: angularjs angularjs-directive

同一元素上的两个指令不能同时具有隔离范围,但它们是否都可以使用与其父元素隔离的相同范围?他们都可以使用绑定到隔离范围的属性吗?

例如,如果我对元素有两个指令

<e-directive a-directive prop="parentProp"/>

一个指令定义了一个带有绑定属性的隔离范围

App.directive('eDirective', function() {
  return {
    restrict: 'E',
    scope: {
      localProp: '=prop'
    },
    ...
  };
});

另一个指令是否获得该范围,是否可以使用绑定属性?

App.directive('aDirective', function() {
  return {
    restrict: 'A',
    link: function postLink(scope, element, attrs) {
        scope.$watch('localProp', function(newProp, oldProp) {
          ...
        }
    },
    ...
  };
});

我的初步尝试(几乎按上述编码)失败。

3 个答案:

答案 0 :(得分:19)

我建议你利用指令之间的沟通&#39;控制器通过辅助指令的require属性。第一个指令(e-directive)保存隔离范围,而第二个指令(a-directive)包含对第一个指令的引用,并通过第一个指令定义的函数设置属性。一小部分样本(see plunker):

<!DOCTYPE html>
<html ng-app="plunker">

  <head>
    <meta charset="utf-8" />
    <title>AngularJS Plunker</title>
    <script>document.write('<base href="' + document.location + '" />');</script>
    <link rel="stylesheet" href="style.css" />
    <script data-require="angular.js@1.2.x" src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.16/angular.min.js" data-semver="1.2.16"></script>
    <script src="app.js"></script>
  </head>

  <body ng-controller="MainCtrl">
    <div e-directive config="parentConfig" a-directive></div>
  </body>

</html>

和javascript:

var app = angular.module('plunker', []);

app.controller('MainCtrl', function($scope) {
  $scope.parentConfig = {};
});

app.controller('ECtrl', function ( $scope ) {
  this.setProp = function(newProp){$scope.config.prop = newProp;};

  $scope.$watch('config', function(newProp, oldProp) {
    console.log(oldProp, newProp);
  });
});

app.directive('eDirective', function() {
  return {
    restrict: 'A',
    scope: {
      config: '='
    },
    controller: 'ECtrl',
    link: function(scope, element, attrs) {
      scope.config.prop ="abc";
    }
  };
});

app.directive('aDirective', function() {
  return {
    restrict: 'A',
    require: 'eDirective',
    link: function(scope, element, attrs,ctrl) {
        ctrl.setProp("def");
    }

  };
});

答案 1 :(得分:1)

指令可以创建一个新的子范围,而不是隔离范围,这两个范围将由两个指令共享。如果您需要修改指令中的parentProp,请注入并使用$parse

<div ng-controller="MyCtrl">
  <e-directive a-directive prop="parentProp"></e-directive>
</div>

使用Javascript:

var app = angular.module('myApp', []);
app.controller('MyCtrl', function($scope) {
    $scope.parentProp = { prop1: 'value1' };
});
app.directive('eDirective', function($parse) {
  return {
    restrict: 'E',
    scope: true,
    template: '<div>dir template: {{eDirLocalProp}}<br>'
          + '<a href ng-click="eDirChange()">change</a></div>',
    link: function(scope, element, attrs) {
      scope.eDirProp1     = 'dirPropValue';
      var model           = $parse(attrs.prop);
      scope.eDirLocalProp = model(scope);
      scope.eDirChange    = function() {
          scope.eDirLocalProp.prop1 = "new value";
      };
    }
  };
});
app.directive('aDirective', function() {
  return {
    scope: true,
    link: function postLink(scope, element, attrs) {
      scope.$watchCollection(attrs.prop, function(newValue) {
        console.log('aDirective', newValue);
      });
    },
  };
});

fiddle

如果两个指令都需要在新子作用域上创建属性,请使用某种命名约定来防止名称冲突。例如,scope.eDirProp1 = ...scope.aDirProp1 = ...

答案 2 :(得分:1)

是的,例如使用element.isolateScope()(或参见fiddle):

<强> HTML

<div ng-app="app" ng-controller="BaseController as baseCtrl">
  <input type="text" ng-model="inputA.value" directive-config="{data: 'bar'}" >
  <input type="text" ng-model="inputB.value" directive-config="{parsers: externalParser, data: 'buzz'}" custom-input >

  <br><br>
  <span style="font-style: italic; font-size: 12px; color: red;">*Open Console to view output</span>
</div>

<强> JS

    (function(angular){
  "use strict";
  angular.module("app", [])

  .controller("BaseController", ['$scope', function($scope){
    $scope.inputA = {value: "This is inputA"};
    $scope.inputB = {value: "This is inputB"};

    $scope.externalParser = function(value) {
      console.log("...parsing value: ", value);
    }
  }])

  .directive("input", [function() {
    return {
      restrict: "E",
      require: '?ngModel',
      scope: {
        directiveConfig: "="
      },
      link: function(scope, element, attrs, ngModelCtrl) {
        console.log("input directive - scope: ", scope);
        console.log("input directive - scope.directiveConfig.data: ", scope.directiveConfig.data);
      }
    }
  }])

  .directive("customInput", [function() {
    return {
      restrict: "A",
      require: '?ngModel',
      link: function(scope, element, attrs, ngModelCtrl) {
        console.log("");
        console.log("--------------------------------------------");
        console.log("customInput directive - scope: ", scope);

        // Use `element.isolateScope()`
        var parentScope = element.isolateScope();
        console.log("customInput directive - parentScope.directiveConfig.parsers: ", parentScope.directiveConfig.parsers);
        console.log("customInput directive - parentScope.directiveConfig.data: ", parentScope.directiveConfig.data);

        console.log("");
        console.log("--------------------------------------------");
        console.warn("DO NOT USE `$$childHead` as it may not target the element you are expecting; use `element.isolateScope()` instead.");
        // DO NOT USE `$$childHead` as it may not be the element you expect
        console.log("customInput directive - scope.$$childHead.directiveConfig.parsers: ", scope.$$childHead.directiveConfig.parsers);
        console.log("customInput directive - scope.$$childHead.directiveConfig.data: ", scope.$$childHead.directiveConfig.data);
      }
    }
  }])

  ;
})(angular)

控制台输出

//input directive - scope:  n {$id: 3, $$childTail: null, $$childHead: null, $$prevSibling: null, $$nextSibling: null…}
//input directive - scope.directiveConfig.data:  bar
//input directive - scope:  n {$id: 4, $$childTail: null, $$childHead: null, $$prevSibling: n, $$nextSibling: null…}
//input directive - scope.directiveConfig.data:  buzz

//--------------------------------------------
//customInput directive - scope:  b {$$childTail: n, $$childHead: n, $$nextSibling: null, $$watchers: Array[4], $$listeners: Object…}
//customInput directive - parentScope.directiveConfig.parsers:  function (value) {
//          console.log("...parsing value: ", value);
//        }
//customInput directive - parentScope.directiveConfig.data:  buzz

//--------------------------------------------
//DO NOT USE `$$childHead` as it may not target the element you are expecting; use `element.isolateScope()` instead.
//customInput directive - scope.$$childHead.directiveConfig.parsers:  undefined
//customInput directive - scope.$$childHead.directiveConfig.data:  bar