从其他控制器调用指令控制器中的方法

use*_*530 118 javascript jquery angularjs angularjs-directive

我有一个有自己的控制器的指令.请参阅以下代码:

var popdown = angular.module('xModules',[]);

popdown.directive('popdown', function () {
    var PopdownController = function ($scope) {
        this.scope = $scope;
    }

    PopdownController.prototype = {
        show:function (message, type) {
            this.scope.message = message;
            this.scope.type = type;
        },

        hide:function () {
            this.scope.message = '';
            this.scope.type = '';
        }
    }

    var linkFn = function (scope, lElement, attrs, controller) {

    };

    return {
        controller: PopdownController,
        link: linkFn,
        replace: true,
        templateUrl: './partials/modules/popdown.html'
    }

});
Run Code Online (Sandbox Code Playgroud)

这是一个错误/通知/警告的通知系统.我想要做的是从另一个控制器(而不是指令控制器)调用show该控制器上的函数.当我这样做时,我还希望我的链接功能检测到某些属性已更改并执行一些动画.

这里有一些代码来举例说明我的要求:

var app = angular.module('app', ['RestService']);

app.controller('IndexController', function($scope, RestService) {
    var result = RestService.query();

    if(result.error) {
        popdown.notify(error.message, 'error');
    }
});
Run Code Online (Sandbox Code Playgroud)

所以打电话时show开启popdown指令控制器,链接功能也应该被触发,执行动画.我怎么能实现这一目标?

sat*_*run 167

这是一个有趣的问题,我开始考虑如何实现这样的事情.

我想出了这个(小提琴) ;

基本上,我没有尝试从控制器调用指令,而是创建了一个模块来存放所有弹出逻辑:

var PopdownModule = angular.module('Popdown', []);
Run Code Online (Sandbox Code Playgroud)

我在模块中放了两个东西,一个factory用于可以在任何地方注入的API,以及directive用于定义实际popdown元素的行为:

工厂只定义了几个函数success,error并跟踪了几个变量:

PopdownModule.factory('PopdownAPI', function() {
    return {
        status: null,
        message: null,
        success: function(msg) {
            this.status = 'success';
            this.message = msg;
        },
        error: function(msg) {
            this.status = 'error';
            this.message = msg;
        },
        clear: function() {
            this.status = null;
            this.message = null;
        }
    }
});
Run Code Online (Sandbox Code Playgroud)

该指令将API注入其控制器,并监视api的更改(为方便起见,我使用bootstrap css):

PopdownModule.directive('popdown', function() {
    return {
        restrict: 'E',
        scope: {},
        replace: true,
        controller: function($scope, PopdownAPI) {
            $scope.show = false;
            $scope.api = PopdownAPI;

            $scope.$watch('api.status', toggledisplay)
            $scope.$watch('api.message', toggledisplay)

            $scope.hide = function() {
                $scope.show = false;
                $scope.api.clear();
            };

            function toggledisplay() {
                $scope.show = !!($scope.api.status && $scope.api.message);               
            }
        },
        template: '<div class="alert alert-{{api.status}}" ng-show="show">' +
                  '  <button type="button" class="close" ng-click="hide()">&times;</button>' +
                  '  {{api.message}}' +
                  '</div>'
    }
})
Run Code Online (Sandbox Code Playgroud)

然后我定义一个app依赖于的模块Popdown:

var app = angular.module('app', ['Popdown']);

app.controller('main', function($scope, PopdownAPI) {
    $scope.success = function(msg) { PopdownAPI.success(msg); }
    $scope.error   = function(msg) { PopdownAPI.error(msg); }
});
Run Code Online (Sandbox Code Playgroud)

HTML看起来像:

<html ng-app="app">
    <body ng-controller="main">
        <popdown></popdown>
        <a class="btn" ng-click="success('I am a success!')">Succeed</a>
        <a class="btn" ng-click="error('Alas, I am a failure!')">Fail</a>
    </body>
</html>
Run Code Online (Sandbox Code Playgroud)

我不确定它是否完全理想,但它似乎是一种与全局弹出指令建立通信的合理方式.

再次,作为参考,小提琴.

  • 通过这种方式可以使用此模块在同一视图中实例化多个指令吗?如何调用此指令的特定实例的成功或错误功能? (11认同)
  • +1一个人永远不应该在指令之外的指令中调用一个函数 - 这是不好的做法.使用服务来管理指令读取的全局状态是非常常见的,这是正确的方法.更多应用程序包括通知队列和模态对话框. (10认同)
  • 非常特别的答案!对于我们这些来自jQuery和Backbone的人来说,这是一个有用的例子 (7认同)
  • @JoshDavidMiller为什么你认为在指令上调用方法是不好的做法?如果一个指令按预期封装了一些DOM逻辑,那么暴露API是否很自然,以便使用它的控制器可以根据需要调用它的方法? (5认同)
  • @ira您可以更改工厂以保留状态和消息对象的映射(或列表),然后在指令上使用name属性来标识列表中您需要的项目.因此,不要在html中调用`success(msg)`,而是调用`sucess(name,msg)`来选择具有正确名称的指令. (3认同)
  • 很好的答案!您已经回答了这个问题以及关于如何编写可以在任何其他项目中删除并按原样使用的良好可重用模块的所有其他问题.非常感谢!我一直在寻找过去2天的这些信息,但是找不到任何可以回答我问题的信息. (2认同)

Aro*_*ron 26

您还可以使用事件来触发Popdown.

这是基于satchmorun解决方案的小提琴.它取消了PopdownAPI,而顶级控制器取代$broadcast了范围链中的"成功"和"错误"事件:

$scope.success = function(msg) { $scope.$broadcast('success', msg); };
$scope.error   = function(msg) { $scope.$broadcast('error', msg); };
Run Code Online (Sandbox Code Playgroud)

Popdown模块然后为这些事件注册处理函数,例如:

$scope.$on('success', function(event, msg) {
    $scope.status = 'success';
    $scope.message = msg;
    $scope.toggleDisplay();
});
Run Code Online (Sandbox Code Playgroud)

这至少在我看来是一个很好的解耦方案.如果由于某种原因这被认为是糟糕的做法,我会让其他人插话.


lua*_*sus 11

你也可能使该指令的控制器父范围,如ngFormname属性的作用:http://docs.angularjs.org/api/ng.directive:ngForm

在这里你可以找到一个非常基本的例子如何实现它http://plnkr.co/edit/Ps8OXrfpnePFvvdFgYJf?p=preview

在这个例子中,我使用myDirective了带有$clear方法的专用控制器(一种非常简单的指令公共API).我可以将此控制器发布到父作用域,并在指令外部使用调用此方法.