【问题标题】:How to expose directive methods using a service如何使用服务公开指令方法
【发布时间】:2017-12-23 21:37:58
【问题描述】:

如何在模块之间不使用 $broadcast 或 '=' 的情况下公开指令方法?

如果有多个指令,则使用$broadcast(事件)将通知所有。它也不能返回值。

暴露指令的函数by html attribute 我认为Angular提供的并不是最好的。

Angular Bootstrap UI 使用服务(我猜):它有一个名为“$uibModal”的服务。 您可以通过注入$uibModal服务来调用Modal Directive的函数“$uibModal.open()”。

这样是对的吗?

【问题讨论】:

  • AngularJS V1.7.1 的发布引入了新的ng-ref 指令。 ng-ref 属性告诉 AngularJS 在当前范围内发布组件的控制器。这对于让诸如音频播放器之类的组件将其 API 暴露给同级组件很有用。它的播放和停止控件可以轻松访问。有关详细信息,请参阅How to expose behavior from a directive with isolated scope?

标签: angularjs angularjs-directive angularjs-service


【解决方案1】:

向服务注册其 API 的指令示例:

app.service("apiService", function() {
    var apiHash = {};
    this.addApi = function (name,api) {
        apiHash[name] = api;
    };
    this.removeApi = function (name) {
        delete apiHash[name];
    };
    this.getApi = function (name) {
        return apiHash[name];
    };
});

app.directive("myDirective", function (apiService) {
    return {
        restrict: 'E',
        scope: {},
        template: `<h1>{{title}}</h1>`,
        link: postLink
    };
    function postLink(scope, elem, attrs)
        var name = attrs.name || 'myDirective';
        var api = {};
        api.setTitle = function(value) {
            scope.title = value;
        };
        apiService.addApi(name, api);
        scope.$on("$destroy", function() {
            apiService.removeApi(name);
        });
    }
});

在应用程序的其他地方,指令的标题可以设置为:

apiService.getApi('myDirective').setTitle("New Title");

请注意,该指令使用由该指令的 name 属性确定的名称注册 api。为了避免内存泄漏,它会在作用域被销毁时取消注册。


更新

如何通过控制器使用它?

  app.controller('home', function($scope,apiService) {
    $scope.title = "New Title";
    $scope.setTitle = function() {
      apiService.getApi('mainTitle').setTitle($scope.title);
    };
  })
  <body ng-controller="home">

    <my-directive name="mainTitle"></my-directive>
    <p>
      <input ng-model="title" />
      <button ng-click="setTitle()">Set Title
      </button>
    </p>
  </body>

The DEMO

angular.module('myApp', [])
  .service("apiService", function() {
    var apiHash = {};
    this.addApi = function(name, api) {
      apiHash[name] = api;
    };
    this.getApi = function(name) {
      return apiHash[name];
    };
  })

.directive("myDirective", function(apiService) {
    return {
      restrict: 'E',
      scope: {},
      template: `<h1>{{title}}</h1>`,
      link: postLink
    };

    function postLink(scope, elem, attrs) {
      var name = attrs.name || 'myDirective';
      var api = {};
      api.setTitle = function(value) {
        scope.title = value;
      };
      apiService.addApi(name, api);
      scope.$on("$destroy", function() {
        apiService.addApi(name, null);
      });
    }
  })
  
  .controller('home', function($scope,apiService) {
    $scope.title = "New Title";
    $scope.setTitle = function() {
      apiService.getApi('mainTitle').setTitle($scope.title);
    };
  })
<script src="//unpkg.com/angular/angular.js"></script>
<body ng-app="myApp" ng-controller="home">
    
    <my-directive name="mainTitle"></my-directive>
    <p>
      <input ng-model="title" />
      <button ng-click="setTitle()">Set Title
      </button>
    </p>
  </body>

【讨论】:

  • 感谢您的回复。我怎么能从控制器中使用它?我在 plunker here 中尝试了一些东西。但没有奏效。我需要的是公开指令的方法以供其他组件使用。就像我们在构建一个包一样。
  • 感谢您的回复!我在这里看到的一个缺点是,就像我在问题中所说的那样,“如果有多个指令,都会通知所有人”。服务“服务”返回一个已由角度引导过程初始化的单例。想一想,听起来我需要返回一个工厂来操作每个指令......
  • 使用指令上的 name 属性来分隔 API。在上面的示例中,服务将 api 存储在名称 mainTitle 下。可以为指令的其他实例使用其他名称。
  • 我知道这里不是说谢谢的地方,但无论如何都要感谢!
【解决方案2】:

<html>
    <style>
        #out {
            width:96%;
            height:25%;
            padding:10px;
            border:3px dashed blue;
            font-family: monospace;
            font-size: 15px;
        }
    </style>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.6/angular.min.js"></script>

    <script>
        var APP = angular.module('MYAPP', []);

        APP.controller('main', ['$scope', '$element', '$compile', 'myService', function($scope, $element, $compile, myService) {
            $scope.test          = 'my Test Controller';
            $scope.directiveTest = "directive test";
            var testSvc = myService.charCount($scope.test);
            $scope.showTestDir = true;
        }])
        .directive('testDirective', ['myService', function(myService) {
            return {
                restrict: 'A',
                replace: true,
                template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
                link: function($scope, el, attrs) {
                    $scope.myTestString = 'string of length 19';
                    $scope.strLen       = myService.charCount( $scope.myTestString );
                }
            }
        }])
        .factory('myService', [function() {
            return {
                charCount: function(inputString) {
                    return inputString.length;
                }
            }
        }])
        .filter('toUpper', function() {
            return function(input) {
                return input.toUpperCase();
            }
        })
        .filter('toLower', function() {
            return function(input) {
                return input.toLowerCase();
            }
        })
        ;
    </script>
        <body ng-app="MYAPP">
        <div id="out" ng-controller="main">
            {{test}} - not filtered
            <br/>
            {{test|toUpper}} - filtered toUpper
            <br/>
            {{test|toLower}} - filtered toLower
            <br/>
            <br/>
            <div test-directive ng-if="showTestDir"></div>
        </div>
    </body>
</html>

【讨论】:

  • 感谢您的回复。但是,如果想从控制器“main”调用 c​​harCount() 呢?
  • 你可以在控制器中注入服务,我已经更新了sn-p
  • 感谢您的回复。但请注意“myService.charCount($scope.test);”对控制器没有影响。真正为“test”设置值的是控制器“$scope.test”的第一行。范围绑定是真正改变价值的东西。请参阅 plunker here
  • 是的,你是对的。 $scope.test 没有被行 var testSvc = myService.charCount($scope.test); 改变我把它放进去是为了证明可以从控制器调用 mySerice。如果你做 console.log(testSvc);您将看到 $scope.test 的字符数,但仅在控制台日志中。
  • 不用担心。上面的例子涵盖了很多概念。我的建议是慢慢来,感受一下。之后你会发现你可以通过一些规则做很多事情。请记住,我们都以不同的速度学习,每个人都有不同的节奏。所以,慢慢来,玩得开心。
【解决方案3】:
.factory('myService', [function() {
    return {
        charCount: function(inputString) {
            return inputString.length;
        }
    }
}])

此服务公开函数 charCount(); 在你的指令中,你必须像这样注入它

.directive('testDirective', ['myService', function(myService) {
    return {
        restrict: 'A',
        replace: true,
        template: "<div>'{{myTestString}}' has length {{strLen}}</div>",
        link: function($scope, el, attrs) {
            $scope.myTestString = 'string of length 19';
            $scope.strLen       = myService.charCount( $scope.myTestString );
        }
    }
}])

当然是这样称呼它

$scope.strLen       = myService.charCount( $scope.myTestString );

【讨论】:

  • 我在一个 plunker here 上发表了你的建议。对我不起作用。也许我忘记了什么。
  • 我添加了一个sn-p(下)它在那里工作,plunker实际上看起来不错,我不知道为什么它在那里不工作,让我再看看
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-12-08
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-21
相关资源
最近更新 更多