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

问题描述:

我有一个有自己的控制器的指令。请参阅下面的代码:来自其他控制器的指令控制器中的调用方法

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

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

+0

你在哪里发出呼叫到页面上的'popdown'指令 - 是它只是在一个地方,其他控制器应该都可以访问它,或在不同的地方有几个弹出窗口? – satchmorun 2013-02-14 21:00:19

+0

我的index.html有这样的:

基本上只有1弹下实例作为它的意思是全局可用。 – user253530 2013-02-14 21:12:10
+1

我想你的意思是写'popdown.show(...)'而不是'popdown.notify(...)'是吗?否则,通知功能有点令人困惑。 – lanoxx 2014-12-08 14:31:01

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

我想出了this (fiddle);

基本上,而不是试图调用来自控制器的指令,我创建了一个模块来容纳所有被弹逻辑:

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

我把两件事情的模块中,这种API的一个factory其能在任何地方注入,并且directive定义实际被弹元素的行为:

工厂只定义了几个功能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(我使用了方便的自举的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模块取决于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); } 
}); 

和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> 

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

再次参考the fiddle

+9

+1不应该在指令外调用指令中的函数 - 这是不好的做法。使用服务来管理指令读取的全局状态是非常常见的,这是正确的方法。更多的应用程序包括通知队列和模态对话框。 – 2013-02-14 22:02:48

+2

优秀的答案!你已经回答了这个问题,以及关于如何编写好的可重用模块的所有其他问题,这些模块可以放在任何其他项目中并按原样使用。非常感谢!过去两天我一直在寻找这些信息,却找不到任何答案来回答我的问题。 – user253530 2013-02-14 22:05:05

+6

非常特殊的答案!对于我们这些来自jQuery和Backbone的人来说,这样一个有用的例子 – Brandon 2013-06-06 16:00:20

您还可以使用事件来触发弹出窗口。

Here's a fiddle基于satchmorun的解决方案。它省去了PopdownAPI和*控制器代替$broadcast S“成功”和“错误”事件向下范围链:

$scope.success = function(msg) { $scope.$broadcast('success', msg); }; 
$scope.error = function(msg) { $scope.$broadcast('error', msg); }; 

的弹下模块然后注册处理函数为这些事件,例如g:

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

这起作用,至少在我看来是一个很好的解耦解决方案。如果出于某种原因被认为是不好的练习,我会让别人加入。

+1

我能想到的一个缺点是,在选定的答案中,您只需要PopdownAPI(易于使用DI)。在这个你需要访问控制器的范围来广播消息。无论如何,它看起来非常简洁。 – Julian 2013-08-28 03:34:58

+0

我喜欢这样比简单用例的服务方式更好,因为它保持复杂性,并且仍然松散耦合 – for3st 2014-10-21 21:40:34

你也可以用name属性暴露指令的控制器父范围,像ngForm作用:http://docs.angularjs.org/api/ng.directive:ngForm

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

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

+0

这需要控制器之间的关系,对吧?由于OP想要一个信息中心,这可能对他而言并不理想。但学习你的方法也很好。它在许多情况下很有用,正如你所说,角本身就是用它的。 – fasfsfgs 2015-07-19 04:09:51

+0

我试图按照satchmorun提供的例子。我在运行时生成一些html,但我没有使用指令的模板。我使用指令的控制器来指定一个函数来从添加的HTML调用,但函数没有被调用。基本上,我有这个指令:directives.directive('abcXyz',函数($ compile {0}返回{ restrict:'AE', require:'ngModel', controller:function($ scope){ $ scope。 function1 = function(){ .. }; },我的html是:“ Mark 2015-08-03 00:51:48

+0

这是唯一可以暴露指令的优雅解决方案api if指令不是单例!我仍然不喜欢使用'$ scope。$ parent [alias]',因为它对我来说就像使用内部的角度api一样。但是仍然找不到更优雅的解决方案其他变体如广播事件或在父控制器中定义空对象以用于指令API更有异味 – djxak 2015-11-26 17:23:22

我有更好的解决方案。

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

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

声明与参数在HTML指令:

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

我的控制器:

app.controller("myController", ['$scope', function ($scope) { 
    // object must be empty initialize,so it can be appended 
    $scope.injectedObject = {}; 

    // now i can directly calling invoke function from here 
    $scope.injectedObject.invoke(); 
}]; 
+0

This basicall y违背了关注原则的分离。您向该指令提供了一个在控制器中实例化的对象,并将该对象的管理职责(即创建调用函数)委派给该指令。在我看来,不是更好的解决方案。 – 2016-08-02 14:54:08