34

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

假设我的 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

5 回答 5

26

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

http://jsfiddle.net/GWCCr/

html:注意新的属性:

<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");
            }
        }
    };
});
于 2013-04-10T20:07:43.147 回答
17

与其试图弄清楚如何调用隐藏在指令中的函数,我认为您应该问自己:为什么我要调用指令中定义的函数?

我能想到的一个原因是:触发指令的某些行为,这些行为也可以由应用程序的用户从指令中触发

如果是这样,那么显而易见且 Angulary 要做的事情是在包含应该对其做出反应的指令的范围内广播一个事件。然后该指令将监听该事件并自行触发其功能。

这有额外的好处:

  • 如果您愿意,可以在事件数据对象中发送参数
  • 您可以在多个指令中触发反应(例如,如果它们形成一个集合)
  • 您可以与范围层次结构深处的指令进行通信(例如,如果该指令在另一个指令中,而该指令在其他指令中,等等),而无需通过每个嵌套指令传递函数回调
  • 它不会破坏指令的隔离

例子

让我们尝试提出一个非常简单的示例:假设我们有一个小部件,它显示从某个地方下载的随机励志名言。它还有一个按钮可以将报价更改为不同的报价。

这是指令的模板:

<p>{{ quote }}</p>
<button ng-click="refreshQuote()"></button>

这是指令的代码:

app.directive("randomQuote", function () {
  return {
    restrict: "E",
    scope: {},
    link: function (scope) {
      scope.refreshQuote = function () {
        scope.quote = ... // some complicated code here
      };
      scope.refreshQuote();
    }
  };
});

请注意,该指令是完全自包含的:它具有隔离范围并自行获取引用。

假设我们还希望能够从控制器刷新报价。这可以像在控制器代码中调用它一样简单:

$scope.$broadcast("refresh-random-quote");

要添加事件处理程序,我们必须将此代码添加到link指令的函数中:

scope.$on("refresh-random-quote", function () {
  scope.refreshQuote();
});

这样,我们创建了一个从控制器到指令的单向通信通道,它不会破坏指令的隔离,并且如果指令嵌套在广播事件的代码的范围层次结构的深处也可以工作.

于 2015-09-19T07:59:31.827 回答
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});
        }
    };
});

小提琴

于 2013-04-10T20:08:44.983 回答
2

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

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

有关详细信息,请参阅

于 2018-12-11T22:15:34.790 回答
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>
于 2017-07-20T21:48:01.530 回答