Can one AngularJS controller call another?
是否可以让一个控制器使用另一个控制器?
例如:
这个HTML文档只打印由
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 | <html xmlns:ng="http://angularjs.org/"> <head> <meta charset="utf-8" /> Inter Controller Communication </head> <body> <p> {{message}} </p> <!-- Angular Scripts --> <script src="http://code.angularjs.org/angular-0.9.19.js" ng:autobind> <script src="js/messageCtrl.js" type="text/javascript"> </body> </html> |
控制器文件包含以下代码:
1 2 3 4 5 6 | function MessageCtrl() { this.message = function() { return"The current date is:" + new Date().toString(); }; } |
只打印当前日期;
如果我要添加另一个控制器,
控制器之间的通信有多种方式。
最好的可能是共享服务:
1 2 3 4 5 6 7 8 9 10 11 | function FirstController(someDataService) { // use the data service, bind to template... // or call methods on someDataService to send a request to server } function SecondController(someDataService) { // has a reference to the same instance of the service // so if the service updates state for example, this controller knows about it } |
另一种方法是在作用域上发出事件:
1 2 3 4 5 6 7 8 9 10 | function FirstController($scope) { $scope.$on('someEvent', function(event, args) {}); // another controller or even directive } function SecondController($scope) { $scope.$emit('someEvent', args); } |
在这两种情况下,您也可以与任何指令通信。
参见此小提琴:http://jsfiddle.net/simpulton/xqdxg/
同时观看以下视频:控制器之间的通信
Html:
1 2 3 4 5 6 7 8 9 10 | <input ng-model="message"> <button ng-click="handleClick(message);">LOG</button> <input ng-model="message"> <input ng-model="message"> |
JavaScript:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 | var myModule = angular.module('myModule', []); myModule.factory('mySharedService', function($rootScope) { var sharedService = {}; sharedService.message = ''; sharedService.prepForBroadcast = function(msg) { this.message = msg; this.broadcastItem(); }; sharedService.broadcastItem = function() { $rootScope.$broadcast('handleBroadcast'); }; return sharedService; }); function ControllerZero($scope, sharedService) { $scope.handleClick = function(msg) { sharedService.prepForBroadcast(msg); }; $scope.$on('handleBroadcast', function() { $scope.message = sharedService.message; }); } function ControllerOne($scope, sharedService) { $scope.$on('handleBroadcast', function() { $scope.message = 'ONE: ' + sharedService.message; }); } function ControllerTwo($scope, sharedService) { $scope.$on('handleBroadcast', function() { $scope.message = 'TWO: ' + sharedService.message; }); } ControllerZero.$inject = ['$scope', 'mySharedService']; ControllerOne.$inject = ['$scope', 'mySharedService']; ControllerTwo.$inject = ['$scope', 'mySharedService']; |
下面是两个共享服务数据的控制器的单页示例:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 | <!doctype html> <html ng-app="project"> <head> Angular: Service example <script src="http://code.angularjs.org/angular-1.0.1.js"> var projectModule = angular.module('project',[]); projectModule.factory('theService', function() { return { thing : { x : 100 } }; }); function FirstCtrl($scope, theService) { $scope.thing = theService.thing; $scope.name ="First Controller"; } function SecondCtrl($scope, theService) { $scope.someThing = theService.thing; $scope.name ="Second Controller!"; } </head> <body> {{name}} <input ng-model="thing.x"/> {{name}} <input ng-model="someThing.x"/> </body> </html> |
另请点击:https://gist.github.com/3595424
如果要将一个控制器调用到另一个控制器,有四种方法可用
1. $rootScope.$emit() and $rootScope.$broadcast()
控制器及其作用域会被破坏,但是$rootscope仍然存在于整个应用程序中,这就是为什么我们采用$rootscope的原因,因为$rootscope是所有作用域的父级。
如果您正在执行从父级到子级的通信,甚至子级希望与其兄弟级通信,则可以使用$broadcast
如果您正在执行从子级到父级的通信,则没有调用兄弟级,那么您可以使用$rootscope.$emit
HTML
1 2 3 4 5 6 7 8 9 10 11 12 13 14 | <body ng-app="myApp"> // ParentCtrl // Sibling first controller // Sibling Second controller // Child controller </body> |
盎格鲁JS码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 | var app = angular.module('myApp',[]);//We will use it throughout the example app.controller('Child', function($rootScope) { $rootScope.$emit('childEmit', 'Child calling parent'); $rootScope.$broadcast('siblingAndParent'); }); app.controller('Sibling1', function($rootScope) { $rootScope.$on('childEmit', function(event, data) { console.log(data + ' Inside Sibling one'); }); $rootScope.$on('siblingAndParent', function(event, data) { console.log('broadcast from child in parent'); }); }); app.controller('Sibling2', function($rootScope) { $rootScope.$on('childEmit', function(event, data) { console.log(data + ' Inside Sibling two'); }); $rootScope.$on('siblingAndParent', function(event, data) { console.log('broadcast from child in parent'); }); }); app.controller('ParentCtrl', function($rootScope) { $rootScope.$on('childEmit', function(event, data) { console.log(data + ' Inside parent controller'); }); $rootScope.$on('siblingAndParent', function(event, data) { console.log('broadcast from child in parent'); }); }); |
在上面的代码控制台$emit'childemit'不会在子同级中调用,它只在父级内部调用,其中$broadcast也会在同级和父级内部调用。这是性能进入操作的地方。$emit是首选的,如果您使用子级到父级通信,因为它跳过了一些脏检查。
2. If Second controller is child, you can use Child Parent communication
它是最好的方法之一,如果你想在孩子想与直接父母交流的地方进行子-父通信,那么它就不需要任何类型的$broadcast或$emit,但是如果你想在父-子之间进行通信,那么你必须使用service或$broadcast
例如HTML:
1 |
角晶状体
1 2 3 4 5 6 | app.controller('ParentCtrl', function($scope) { $scope.value='Its parent'; }); app.controller('ChildCtrl', function($scope) { console.log($scope.value); }); |
每当您使用子到父通信时,AngularJS将搜索子对象内的变量,如果该变量不在子对象内,则它将选择查看父对象控制器内的值。
3.Use Services
AngularJS使用服务体系结构支持"分离关注点"的概念。服务是JavaScript函数,只负责执行特定的任务。这使它们成为一个可维护和可测试的单独实体。使用AngularJS的依赖项注入Mecahnism来注入服务。
盎格鲁JS代码:
1 2 3 4 5 6 7 8 9 10 11 | app.service('communicate',function(){ this.communicateValue='Hello'; }); app.controller('ParentCtrl',function(communicate){//Dependency Injection console.log(communicate.communicateValue+" Parent World"); }); app.controller('ChildCtrl',function(communicate){//Dependency Injection console.log(communicate.communicateValue+" Child World"); }); |
它将提供输出hello child world和hello parent world。根据服务单例的角度文档,依赖于服务的每个组件都会获得对服务工厂生成的单个实例的引用。
4.Kind of hack - with the help of angular.element()
此方法通过id/unique类从元素中获取scope()。angular.element()方法返回element,scope()使用一个控制器内部的$scope变量给另一个变量的$scope变量,这不是一个好的做法。
HTML:
1 2 3 4 | {{varParent}} <span ng-click='getValueFromChild()'>Click to get ValueFormChild</span> {{varChild}} <span ng-click='getValueFromParent()'>Click to get ValueFormParent </span> |
Angularjs:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | app.controller('ParentCtrl',function($scope){ $scope.varParent="Hello Parent"; $scope.getValueFromChild=function(){ var childScope=angular.element('#child').scope(); console.log(childScope.varChild); } }); app.controller('ChildCtrl',function($scope){ $scope.varChild="Hello Child"; $scope.getValueFromParent=function(){ var parentScope=angular.element('#parent').scope(); console.log(parentScope.varParent); } }); |
在上面的代码中,控制器在HTML上显示自己的值,当您单击文本时,您将相应地在控制台中获得值。如果单击父控制器范围,浏览器将控制台子控件和viceversa的值。
如果您希望发出广播事件以在控制器之间共享数据或调用功能,请查看此链接:并检查
如果firstctrl的作用域是secondctrl作用域的父级,您的代码应该通过在firstctrl中用$broadcast替换$emit来工作:
1 2 3 4 5 6 7 | function firstCtrl($scope){ $scope.$broadcast('someEvent', [1,2,3]); } function secondCtrl($scope){ $scope.$on('someEvent', function(event, mass) {console.log(mass)}); } |
如果您的作用域之间没有父子关系,您可以将$rootscope插入控制器,并将事件广播到所有子作用域(也就是secondctrl)。
1 2 3 | function firstCtrl($rootScope){ $rootScope.$broadcast('someEvent', [1,2,3]); } |
最后,当需要将事件从子控制器分派到向上的作用域时,可以使用$scope.$emit。如果firstctrl的作用域是secondctrl作用域的父级:
1 2 3 4 5 6 7 | function firstCtrl($scope){ $scope.$on('someEvent', function(event, data) { console.log(data); }); } function secondCtrl($scope){ $scope.$emit('someEvent', [1,2,3]); } |
还有两个问题:(非服务方法)
1)对于父子控制器-使用父控制器的
2)在非相关控制器上使用
实际上,使用emit和broadcast效率很低,因为事件在作用域层次结构中上下冒泡,这很容易降低为复杂应用程序的性能装瓶。
我建议使用服务。以下是我最近在我的一个项目中实现它的方法-https://gist.github.com/3384419。
基本思想-将pub子/事件总线注册为服务。然后在需要订阅或发布事件/主题的地方插入该事件总线。
我不知道这是否超出了标准,但是如果您的所有控制器都在同一个文件中,那么您可以这样做:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 | app = angular.module('dashboardBuzzAdmin', ['ngResource', 'ui.bootstrap']); var indicatorsCtrl; var perdiosCtrl; var finesCtrl; app.controller('IndicatorsCtrl', ['$scope', '$http', function ($scope, $http) { indicatorsCtrl = this; this.updateCharts = function () { finesCtrl.updateChart(); periodsCtrl.updateChart(); }; }]); app.controller('periodsCtrl', ['$scope', '$http', function ($scope, $http) { periodsCtrl = this; this.updateChart = function() {...} }]); app.controller('FinesCtrl', ['$scope', '$http', function ($scope, $http) { finesCtrl = this; this.updateChart = function() {...} }]); |
如您所见,指示符sctrl在调用updateCharts时调用其他两个控制器的updateChart函数。
我也知道这一点。
1 | angular.element($('#__userProfile')).scope().close(); |
但我不太喜欢使用它,因为我不喜欢在角度代码中使用jquery选择器。
可以在父控制器(messagectrl)中插入"$controller"服务,然后使用以下方法实例化/插入子控制器(datecrl):
现在,您可以通过调用子控制器的方法来访问子控制器中的数据,因为它是一个服务。如果有任何问题,请告诉我。
有一种方法不依赖于服务,即
对于父/子情况,使用如下:
1 |
使它工作的要点是:在parent指令中,使用要调用的方法,应该在
1 2 3 4 5 | controller: function($scope) { this.publicMethodOnParentDirective = function() { // Do something } } |
在子指令定义上,您可以使用
1 2 3 4 5 6 7 | require: '^parentDirective', template: '<span ng-click="onClick()">Click on this to call parent directive</span>', link: function link(scope, iElement, iAttrs, parentController) { scope.onClick = function() { parentController.publicMethodOnParentDirective(); } } |
以上内容请访问http://plnkr.co/edit/poeq460vmqer8gl9w8oz?P=预览
类似地使用了兄弟指令,但同一元素上的两个指令都是:
1 |
用于在
1 2 3 4 5 | controller: function($scope) { this.publicMethod = function() { // Do something } } |
在Directive2中,可以使用
1 2 3 4 5 6 7 | require: 'directive1', template: '<span ng-click="onClick()">Click on this to call sibling directive1</span>', link: function link(scope, iElement, iAttrs, siblingController) { scope.onClick = function() { siblingController.publicMethod(); } } |
这可以在http://plnkr.co/edit/mud2snf9zvadfndxq85w上看到?P =预览。
这个的用途?
父元素:子元素需要向父元素"注册"自己的任何情况。很像NGModel和NGForm之间的关系。这些可以添加一些可能影响模型的行为。您可能也有一些纯粹基于DOM的东西,其中父元素需要管理某些子元素的位置,比如管理滚动或对滚动做出反应。
同级:允许指令修改其行为。ngmodel是一个经典的例子,用于向输入上使用的ngmodel添加解析器/验证。
在角度1.5中,可以通过执行以下操作来实现:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | (function() { 'use strict'; angular .module('app') .component('parentComponent',{ bindings: {}, templateUrl: '/templates/products/product.html', controller: 'ProductCtrl as vm' }); angular .module('app') .controller('ProductCtrl', ProductCtrl); function ProductCtrl() { var vm = this; vm.openAccordion = false; // Capture stuff from each of the product forms vm.productForms = [{}]; vm.addNewForm = function() { vm.productForms.push({}); } } }()); |
这是父组件。在这里,我创建了一个函数,它将另一个对象推送到我的
现在我们可以创建另一个组件来使用
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | (function() { 'use strict'; angular .module('app') .component('childComponent', { bindings: {}, require: { parent: '^parentComponent' }, templateUrl: '/templates/products/product-form.html', controller: 'ProductFormCtrl as vm' }); angular .module('app') .controller('ProductFormCtrl', ProductFormCtrl); function ProductFormCtrl() { var vm = this; // Initialization - make use of the parent controllers function vm.$onInit = function() { vm.addNewForm = vm.parent.addNewForm; }; } }()); |
在这里,子组件正在创建对父组件函数
下面是一个与角度js无关的
搜索参数控制器
1 2 3 4 5 6 7 8 9 10 | //Note: Multiple entities publish the same event regionButtonClicked: function () { EM.fireEvent('onSearchParamSelectedEvent', 'region'); }, plantButtonClicked: function () { EM.fireEvent('onSearchParamSelectedEvent', 'plant'); }, |
Search Choices Controller
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 | //Note: It subscribes for the 'onSearchParamSelectedEvent' published by the Search Param Controller localSubscribe: function () { EM.on('onSearchParamSelectedEvent', this.loadChoicesView, this); }); loadChoicesView: function (e) { //Get the entity name from eData attribute which was set in the event manager var entity = $(e.target).attr('eData'); console.log(entity); currentSelectedEntity = entity; if (entity == 'region') { $('.getvalue').hide(); this.loadRegionsView(); this.collapseEntities(); } else if (entity == 'plant') { $('.getvalue').hide(); this.loadPlantsView(); this.collapseEntities(); } }); |
事件管理器
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 | myBase.EventManager = { eventArray:new Array(), on: function(event, handler, exchangeId) { var idArray; if (this.eventArray[event] == null) { idArray = new Array(); } else { idArray = this.eventArray[event]; } idArray.push(exchangeId); this.eventArray[event] = idArray; //Binding using jQuery $(exchangeId).bind(event, handler); }, un: function(event, handler, exchangeId) { if (this.eventArray[event] != null) { var idArray = this.eventArray[event]; idArray.pop(exchangeId); this.eventArray[event] = idArray; $(exchangeId).unbind(event, handler); } }, fireEvent: function(event, info) { var ids = this.eventArray[event]; for (idindex = 0; idindex < ids.length; idindex++) { if (ids[idindex]) { //Add attribute eData $(ids[idindex]).attr('eData', info); $(ids[idindex]).trigger(event); } } } }; |
全球的
1 | var EM = myBase.EventManager; |