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

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/15934500/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-10-27 02:39:50  来源:igfitidea点击:

How to expose behavior from a directive with isolated scope?

javascriptangularjsangularjs-directive

提问by Konrad Garus

How can I expose a method from a directive? I know that I should use attributes for data, but I really want to expose behavior, not data. Something that the parent controller can call.

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

Let's say my DOM looks like:

假设我的 DOM 看起来像:

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

JavaScript:

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/

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

If the scopeis commented out (i.e. the directive does not have isolated scope), it works just fine. When I press the button, myfnis called and logs to console.

如果scope被注释掉(即指令没有独立的作用域),它就可以正常工作。当我按下按钮时,myfn被调用并登录到控制台。

As soon as I uncomment scope, it doesn't work. myfnis defined on child scope and not easily available to the parent.

一旦我取消注释scope,它就不起作用。myfn是在子作用域上定义的,父级不容易获得。

In my case I think that polluting the parent scope is a bad idea and I would really like to avoid it.

就我而言,我认为污染父作用域是一个坏主意,我真的很想避免它。

So, how can I expose a function from directive to the parent controller? Or: How can I invoke a method on directive from parent controller?

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

回答by Roy Truelove

You can do this with an isolated scope by setting up a variable in the scope that's two-way bound to the controller (using '='). In your directive you can then assign the function to that variable, and angular will use the binding to find the corresponding variable in your controller. That variable will point to a function that your controller can call.

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

http://jsfiddle.net/GWCCr/

http://jsfiddle.net/GWCCr/

html: Note the new attrib:

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:

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

回答by DzinX

Rather than trying to figure out how to call a function hidden inside a directive, I think you should be asking yourself: why do I want to call a function defined in a directive?

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

One reason I can think of is: to trigger some behaviour of the directive that could also be triggered by the user of the application from within the directive.

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

If so, the obvious and Angulary thing to do would be to broadcast an eventon a scope that contains the directive that should react to it. Then the directive would listen to that event and trigger its function by itself.

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

This has additional benefits:

这有额外的好处:

  • you can send parameters in the event data object if you want to
  • you can trigger a reaction in more than one directive (e.g. if they form a collection)
  • you can communicate with a directive that is deep in the scope hierarchy (e.g. if the directive is inside another directive that is inside other directives, etc.) without passing a function callback through each nested directive
  • it doesn't break the isolation of the directive
  • 如果需要,您可以在事件数据对象中发送参数
  • 您可以在多个指令中触发反应(例如,如果它们形成一个集合)
  • 您可以与位于作用域层次结构深处的指令进行通信(例如,如果该指令位于其他指令内的另一个指令内,等等),而无需通过每个嵌套指令传递函数回调
  • 它不会破坏指令的隔离

Example

例子

Let's try to come up with a very simple example: suppose we have a widget that displays a random inspirational quote downloaded from somewhere. It also has a button to change the quote to a different one.

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

Here's the directive's template:

这是指令的模板:

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

And here's the directive's code:

这是指令的代码:

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

Note that the directive is entirely self-contained: it has an isolate scope and does the quote-fetching by itself.

请注意,该指令是完全独立的:它有一个隔离的范围,并自行执行引用提取。

Let's suppose we also want to be able to refresh the quote from the controller. This could be as simple as calling this in the controller code:

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

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

To add the event handler, we must add this code to the linkfunction of the directive:

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

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

This way, we've created a one-way communication channel from the controller to the directive that doesn't break the isolation of the directive, and also works if the directive is nested deep in the scope hierarchy of the code that broadcasts the event.

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

回答by Mark Rajcok

How can I expose a function from directive to the parent controller?
Or: How can I invoke a method on directive from parent controller?

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

Well, I don't think you should be trying to do this (i.e., coupling controller behavior to a directive), but if you must... here's one way you can do it: pass a controller function to your directive, which the directive can call to notify the controller of the directive function:

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

<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

小提琴

回答by saulsluz

To contribuite, @georgeawg gave me a cool solution using Service to do the job. This way you can handle multiple directives on same page.

为了做出贡献,@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>

回答by georgeawg

The release of AngularJS V1.7.1*introduces the new ng-ref directive.

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

The ng-refattribute tells AngularJS to publish the controller of a component on the current scope. This is useful for having a component such as an audio player expose its API to sibling components. Its play and stop controls can be easily accessed.

NG-REF属性告诉AngularJS发布一个组件的控制器上的当前范围。这对于让诸如音频播放器之类的组件向同级组件公开其 API 非常有用。可以轻松访问其播放和停止控件。

For more information, see

有关更多信息,请参阅