web-dev-qa-db-fra.com

Plusieurs directives pour un élément peuvent-elles partager une étendue isolée?

Deux directives sur le même élément ne peuvent pas avoir une portée isolée, mais peuvent-elles toutes deux utiliser la même portée isolée de leur parent? Et peuvent-ils tous les deux utiliser des propriétés liées à la portée isolée?

Par exemple, si j'ai deux directives sur un élément

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

Et une directive définit une portée isolée avec une propriété liée

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

L'autre directive a-t-elle cette portée et peut-elle utiliser la propriété liée?

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

Ma tentative initiale (à peu près codée comme ci-dessus) a échoué.

15
Robert Antonucci

Je vous suggère d'utiliser la communication entre les contrôleurs des directives via la propriété require de la directive secondaire. La première directive (directive électronique) contient la portée isolée, tandis que la deuxième directive d'assistance (directive a) fait référence à la première directive et définit les propriétés via les fonctions définies dans la première directive. Un petit échantillon serait ( voir 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="[email protected]" 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>

et le 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");
    }

  };
});
19
zszep

Au lieu d'une portée d'isolement, les directives peuvent créer une nouvelle portée enfant, qui sera partagée par les deux directives. Si vous avez besoin de modifier parentProp dans une directive, injectez et utilisez $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

Si les deux directives doivent créer des propriétés sur la nouvelle étendue enfant, utilisez une sorte de convention de dénomination pour éviter les conflits de noms. Par exemple, scope.eDirProp1 = ... and scope.aDirProp1 = ....

1
Mark Rajcok

Oui en utilisant element.isolateScope() par exemple (ou voir violon ):

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)

sortie de la console

//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
1
Eric Norcross