传递父控制器方法以转换子指令

时间:2015-12-11 03:01:34

标签: javascript angularjs directive angularjs-ng-transclude

我正在尝试从transcluding child指令的link函数访问父指令的控制器方法,但是没有运气。当我将子项作为父模板的一部分包含在内或从父ng控制器传递它时,它可以工作。不应该转换在角度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

1 个答案:

答案 0 :(得分:0)

问题

由于您将scope: { ctrlFn: '&', }放在outerDirective上,因此会创建一个isolate scope

  

“隔离”作用域与常规作用域的不同之处在于,它不原型地继承自其父作用域。这在创建可重用的组件时非常有用,该组件不应意外读取或修改父范围中的数据。请注意,没有templatetemplateUrl的isolate作用域指令不会将isolate作用域应用于其子元素。

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

但是,这个范围被被包容的孩子忽略了。超越时,您会得到一个特殊的transclusion scope,它会特别忽略此隔离范围:

  

此范围是特殊的,因为它是指令范围的子级(因此在删除指令范围时会被销毁),但它继承了从其获取范围的属性。

这意味着传递给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。我也可以将控制器也直接存储在示波器上,并为innerDirective保存几个LOC:

{
  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交互的操作,而不是用来间接连接根应用程序控制器的任何操作。

Updated JSBin