2

我正在尝试从包含子指令的链接函数访问父指令的控制器方法,但没有运气。当我将孩子作为父母模板的一部分或从父母 ng-controller 传入时,它会起作用。不应该在 Angular 1.4 中创建子范围吗?

Javascript:

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

app.controller('AppController', function($scope) {
  $scope.ctrlFn = function() {
    alert('hello');
  };
});

app.directive('outerDirective', function() {
  return {
    restrict: 'A',
    scope: {
      ctrlFn : '&'
    },
    controller: 'AppController',
    transclude:true,
    template: '<div ng-transclude></div>',
    link: function(scope, element, attributes) {
      scope.outerFunction = function() {

        scope.ctrlFn();
      };
    }
  };
});

app.directive('innerDirective', function() {
  return {
    scope: {
      ctrlFn : '&'
    },
    replace:true,
    template: '<button ng-click="innerFunction()">Child Directive</button>',
    link: function(scope, element, attributes) {
      scope.innerFunction = function() {

        scope.ctrlFn();
      };
    }
  };
});

HTML:

<div id="app" ng-app="app">
  <div outer-directive ctrl-fn="ctrlFn">

    <div inner-directive ctrl-fn="ctrlFn()"></div>

  </div>
</div>

JSBin

4

1 回答 1

0

问题

因为你穿上scope: { ctrlFn: '&', }outerDirective它创建了一个隔离范围

“隔离”作用域与普通作用域的不同之处在于它在原型上并不从其父作用域继承。这在创建可重用组件时很有用,这些组件不应意外读取或修改父范围内的数据。请注意,没有templateor的隔离范围指令不会templateUrl将隔离范围应用于其子元素。

隔离作用域的一个用例是能够scope.$watch在链接函数的作用域上进行表达式,即使您的指令没有模板,这也很有用。隔离范围的另一个用途是为指令模板中的指令和插值提供数据(如果有的话)。

然而,这个范围被嵌入的孩子忽略了。当你嵌入时,你会得到一个特殊的嵌入范围,它专门忽略这个隔离范围:

这个作用域是特殊的,因为它是指令作用域的一个子对象(因此当指令的作用域被破坏时也会被破坏),但它继承了它所在作用域的属性。

这意味着scope传递给您的link函数仅对您的模板可见。任何被嵌入的东西都会看到你的指令的父范围并忽略隔离范围。

解决方案

指令相互通信的一种方式是require. require 指令可以将容器指令的控制器注入到任何子函数中link。此外,require通过 DOM 树而不是范围继承树搜索祖先。所以当你需要一个控制器时,被嵌入的内容可以得到嵌入它的指令的控制器。例如,您的代码可以调整为以这种方式使用控制器:

JavaScript:

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

app.controller('AppController', function($scope) {
  $scope.ctrlFn = function() {
    alert('hello');
  };
});

app.directive('outerDirective', function() {
  return {
    restrict: 'A',
    scope: {
      ctrlFn : '='
    },
    controller: function OuterDirectiveController($scope) {
      this.outerFunction = function () {
        return $scope.ctrlFn();
      };
    },
    transclude:true,
    template: '<div ng-transclude></div>'
  };
});

app.directive('innerDirective', function() {
  return {
    replace:true,
    template: '<button ng-click="innerFunction()">Child Directive</button>',
    link: function(scope, element, attributes, outerDirective) {
      scope.innerFunction = function() {
        outerDirective.outerFunction();
      };
    },
    scope: {},
    require: '^outerDirective'
  };
});

请注意,我还将 更改为ctrlFn: '&'ctrlFn: '='因为'&'它适用于您想要在调用者中执行表达式的情况。例如,当您执行类似<my-directive on-something-happened="x = $event.value"/>. 但是,您尝试将函数作为值传递,而不是尝试传递可执行的角度表达式。

注意我设置scope: {}了,innerDirective但我把它留空。这向 AngularJS 表明我想要一个隔离作用域——我不想覆盖父作用域中的任何值。然后在我的链接函数中,我填充scope.innerFunction以便我的模板可以访问innerFunction引用所需控制器的内容。我也可以直接将控制器存储在示波器上,并保存几个 LOC innerDirective

{
  template: '<button ng-click="outerDirective.outerFunction()">Child Directive</button',
  link: function (scope, element, attributes, outerDirective) {
    scope.outerDirective = outerDirective;
  },
  // (other keys omitted for brevity).
}

HTML:

<div id="app" ng-app="app" ng-controller="AppController">
  <div outer-directive ctrl-fn="ctrlFn">
    <div inner-directive></div>
  </div>
</div>

另请注意,我更改了您的控制器。您AppController现在使用 . 连接到根应用程序元素ng-controller="AppController"。这允许您从应用程序控制器设置范围。然后,每个指令根据传递给它们的数据独立运行,而不是用作根范围。我认为指令的通常用例是可重用模板或执行任何需要直接与 DOM 交互而不是使用一次来间接附加根应用程序控制器的事情。

更新了 JSBin

于 2018-07-20T19:59:49.617 回答