【问题标题】:Can multiple directives for one element share an isolated scope?一个元素的多个指令可以共享一个隔离范围吗?
【发布时间】:2018-03-22 21:21:16
【问题描述】:

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

例如,如果我在一个元素上有两个指令

<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) {
          ...
        }
    },
    ...
  };
});

我最初的尝试(几乎像上面那样编码)失败了。

【问题讨论】:

  • 如果您对aDirective 重复scope: { localProp: '=prop' } 会发生什么?
  • 这指示两个指令各自创建自己的隔离范围,这不仅不是我想要的,而且还会导致“多个指令要求隔离范围”错误。

标签: angularjs angularjs-directive


【解决方案1】:

我建议您通过辅助指令的 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");
    }

  };
});

【讨论】:

  • 我承认,我希望有一种方法来共享范围,特别是当文档看到“一个元素只能有一个范围”之类的东西时,但如果那不可能,这可以绝对解决我的问题。
【解决方案2】:

指令可以创建一个新的子范围,而不是隔离范围,这将由两个指令共享。如果需要修改指令中的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 = ...

【讨论】:

    【解决方案3】:

    可以,例如使用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
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2017-01-17
      • 1970-01-01
      • 1970-01-01
      • 2021-08-07
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多