从另一个控制器调用指令控制器中的方法

时间:2013-02-14 20:29:16

标签: 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'
    }

});

这是一个错误/通知/警告的通知系统。我想要做的是从另一个控制器(而不是指令控制器)调用此控制器上的函数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');
    }
});

因此,当在show指令控制器上调用popdown时,也应该触发链接功能并执行动画。我怎么能做到这一点?

4 个答案:

答案 0 :(得分:167)

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

我想出了this (fiddle);

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

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

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

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

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

该指令将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>'
    }
})

然后我定义了一个依赖于app的{​​{1}}模块:

Popdown

HTML看起来像:

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

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

再次,作为参考,the fiddle

答案 1 :(得分:26)

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

Here's a fiddle基于satchmorun的解决方案。它取消了PopdownAPI,而顶级控制器取代了范围链中的$broadcast'成功'和'错误'事件:

$scope.success = function(msg) { $scope.$broadcast('success', msg); };
$scope.error   = function(msg) { $scope.$broadcast('error', msg); };
然后,Popdown模块为这些事件注册处理函数,例如:

$scope.$on('success', function(event, msg) {
    $scope.status = 'success';
    $scope.message = msg;
    $scope.toggleDisplay();
});

这至少在我看来是一个很好的解耦方案。如果出于某种原因认为这是不好的做法,我会让其他人参与进来。

答案 2 :(得分:11)

您还可以将指令的控制器公开给父作用域,例如ngForm name属性:http://docs.angularjs.org/api/ng.directive:ngForm

在这里,您可以找到一个非常基本的示例,了解如何实现http://plnkr.co/edit/Ps8OXrfpnePFvvdFgYJf?p=preview

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

答案 3 :(得分:3)

我有更好的解决方案。

这是我的指令,我在指令中注入了对象引用,并通过在指令代码中添加invoke函数来扩展它。

[{"id":0,"text":"test1","children":[{"text":"1.docx","icon":"jstree-file"}],"path":"folder"},{"id":1,"text":"test2","children":[{"id":0,"text":"New folder","children":[],"path":"test2"},{"text":"2.txt","icon":"jstree-file"}],"path":"folder"}]

使用参数声明HTML中的指令:

app.directive('myDirective', function () {
    return {
        restrict: 'E',
        scope: {
        /*The object that passed from the cntroller*/
        objectToInject: '=',
        },
        templateUrl: 'templates/myTemplate.html',

        link: function ($scope, element, attrs) {
            /*This method will be called whet the 'objectToInject' value is changes*/
            $scope.$watch('objectToInject', function (value) {
                /*Checking if the given value is not undefined*/
                if(value){
                $scope.Obj = value;
                    /*Injecting the Method*/
                    $scope.Obj.invoke = function(){
                        //Do something
                    }
                }    
            });
        }
    };
});

我的控制器:

<my-directive object-to-inject="injectedObject"></ my-directive>