如何从具有隔离范围的指令中公开行为?

时间:2013-04-10 19:17:42

标签: javascript angularjs angularjs-directive

如何从指令中公开方法?我知道我应该使用数据属性,但我真的想公开行为,而不是数据。父控制器可以调用的东西。

假设我的DOM看起来像:

<div ng-app="main">
    <div ng-controller="MyCtrl">
        <button ng-click="call()" >Call</button>
        <div id="container" my-directive> </div>
    </div>
</div>

JavaScript的:

angular.module("main", []).controller("MyCtrl", function($scope) {
    $scope.call = function() {
        $scope.myfn();
    };
}).directive("myDirective", function() {
    return {
        // scope: {},
        controller: function($scope) {
            $scope.myfn = function() {
                console.log("myfn called");
            }
        }
    };
});

jsFiddle:http://jsfiddle.net/5gDjQ/7/

如果scope被注释掉(即指令没有隔离范围),它就可以了。当我按下按钮时,会调用myfn并登录到控制台。

一旦我取消注释scope,它就无效。 myfn是在子范围上定义的,父母不容易获得。

在我的情况下,我认为污染父范围是一个坏主意,我真的想避免它。

那么,我如何将一个函数从指令公开给父控制器?或者:如何从父控制器调用指令上的方法?

4 个答案:

答案 0 :(得分:26)

您可以通过在与控制器双向绑定的范围中设置变量(使用“=”)来使用隔离范围执行此操作。在您的指令中,您可以将该函数分配给该变量,而angular将使用该绑定在您的控制器中查找相应的变量。该变量将指向控制器可以调用的函数。

http://jsfiddle.net/GWCCr/

html:注意新的attrib:

<div ng-app="main">
    <div ng-controller="MyCtrl">
        <button ng-click="call()" >Call</button>
        <div id="container" my-directive my-fn="fnInCtrl"> </div>
    </div>
</div>

JS:

angular.module("main", []).controller("MyCtrl", function($scope) {
    $scope.call = function() {
        $scope.fnInCtrl();
    };
}).directive("myDirective", function() {
    return {
        scope: {
            myFn: '='
        },
        controller: function($scope) {
            $scope.myFn = function() {
                console.log("myfn called");
            }
        }
    };
});

答案 1 :(得分:7)

  

如何将指令中的函数公开给父控制器?
或者:如何从父控制器调用指令上的方法?

好吧,我认为你不应该尝试这样做(即将控制器行为耦合到指令),但是如果你必须......这是你可以做到的一种方法:将控制器函数传递给你的指令,该指令可以调用以通知控制器指令函数:

<div id="container" my-directive cb="setDirectiveFn(fn)"></div>

directive("myDirective", function() {
    return {
       scope: { cb: '&' },
        controller: function($scope) {
            $scope.myfn = function() {
                console.log("myfn called");
            }
            $scope.cb({fn: $scope.myfn});
        }
    };
});

Fiddle

答案 2 :(得分:1)

为了贡献,@ georgeawg给了我一个很酷的解决方案,使用Service来完成这项工作。这样您就可以在同一页面上处理多个指令。

<html ng-app="myApp">
<head>
  <script src="https://opensource.keycdn.com/angularjs/1.6.5/angular.min.js"></script>
</head>
<body ng-controller="mainCtrl">
  <h1>^v1.6.0 ($postLink hook required)</h1>
  <my-directive name="sample1" number="number1"></my-directive>
  <my-directive name="sample2" number="number2"></my-directive>
</body>
<script>
  angular.module('myApp', [])
    .controller('mainCtrl', ['$scope', 'myDirectiveFactory', function ($scope, myDirectiveFactory) {
      $scope.number1 = 10
      $scope.number2 = 0
      this.$postLink = function () {
        myDirectiveFactory.get('sample2')
          .increment()
          .increment()
          .increment()
          .increment()
        myDirectiveFactory.get('sample1')
          .increment()
          .increment()
        myDirectiveFactory.get('sample2')
        .decrement()
      }
    }])
    .factory('myDirectiveFactory', function () {
      var instance = {}
      return {
        get: function (name) {
          return instance[name]
        },
        register: function (name, value) {
          return instance[name] = value
        },
        destroy: function (name) {
          delete instance[name]
        }
      }
    })
    .controller('myDirectiveCtrl', ['$scope', 'myDirectiveFactory', function ($scope, myDirectiveFactory) {
      $scope.name = $scope.name || 'myDirective'
      $scope.$on('$destroy', function () {
        myDirectiveFactory.destroy($scope.name)
      })
      var service = {
        increment: function () {
          $scope.number++
          return this
        },
        decrement: function () {
          $scope.number--
          return this
        }
      }
      myDirectiveFactory.register($scope.name, service)
    }])
    .directive('myDirective', [function () {
      return {
        controller: 'myDirectiveCtrl',
        restrict: 'E',
        scope: {
          number: '<',
          name: '@?'
        },
        template: '<p> {{ number }} </p>'
      }
    }])
</script>
</html>

答案 3 :(得分:0)

AngularJS V1.7.1的发布 * 引入了新的ng-ref directive

ng-ref属性告诉AngularJS在当前作用域上发布组件的控制器。这对于使诸如音频播放器之类的组件将其API暴露给同级组件很有用。可以轻松访问其播放和停止控件。

有关更多信息,请参见