一个控制器可以通话吗?
一个控制器可以使用另一个控制器吗?
例如:
这个HTML文档只是在messageCtrl.js
文件中打印由MessageCtrl
控制器传递的MessageCtrl
。
<html xmlns:ng="http://angularjs.org/"> <head> <meta charset="utf-8" /> <title>Inter Controller Communication</title> </head> <body> <div ng:controller="MessageCtrl"> <p>{{message}}</p> </div> <!-- Angular Scripts --> <script src="http://code.angularjs.org/angular-0.9.19.js" ng:autobind></script> <script src="js/messageCtrl.js" type="text/javascript"></script> </body> </html>
控制器文件包含以下代码:
function MessageCtrl() { this.message = function() { return "The current date is: " + new Date().toString(); }; }
哪个只是打印当前的date;
如果我要添加另一个控制器, DateCtrl
将date以特定的格式交还给MessageCtrl
,那么怎么做呢? DI框架似乎与XmlHttpRequests
和访问服务有关。
如何在控制器之间进行通信有多种方式。
最好的一个可能是共享服务:
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 }
另一种方法是发射一个事件的范围:
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/
另请观看以下video: 在控制器之间进行通信
HTML:
<div ng-controller="ControllerZero"> <input ng-model="message" > <button ng-click="handleClick(message);">LOG</button> </div> <div ng-controller="ControllerOne"> <input ng-model="message" > </div> <div ng-controller="ControllerTwo"> <input ng-model="message" > </div>
JavaScript的:
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'];
以下是共享服务数据的两个控制器的单页示例:
<!doctype html> <html ng-app="project"> <head> <title>Angular: Service example</title> <script src="http://code.angularjs.org/angular-1.0.1.js"></script> <script> 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!"; } </script> </head> <body> <div ng-controller="FirstCtrl"> <h2>{{name}}</h2> <input ng-model="thing.x"/> </div> <div ng-controller="SecondCtrl"> <h2>{{name}}</h2> <input ng-model="someThing.x"/> </div> </body> </html>
也在这里: https : //gist.github.com/3595424
如果你想调用另一个控制器有四种方法可用
- $ rootScope。$ emit()和$ rootScope。$ broadcast()
- 如果第二个控制器是小孩,则可以使用父子通信。
- 使用服务
- 一种黑客 – 在angular.element()的帮助下
1. $ rootScope。$ emit()和$ rootScope。$ broadcast()
控制器及其作用域可能被破坏,但$ rootScope保留在应用程序中,这就是为什么我们要使用$ rootScope,因为$ rootScope是所有作用域的父级。
如果你正在进行从父母到孩子的沟通,甚至孩子想与兄弟姐妹沟通,你可以使用$广播
如果你正在执行从孩子到父母的沟通,没有兄弟姐妹调用,那么你可以使用$ rootScope。$ emit
HTML
<body ng-app="myApp"> <div ng-controller="ParentCtrl" class="ng-scope"> // ParentCtrl <div ng-controller="Sibling1" class="ng-scope"> // Sibling first controller </div> <div ng-controller="Sibling2" class="ng-scope"> // Sibling Second controller <div ng-controller="Child" class="ng-scope"> // Child controller </div> </div> </div> </body>
Angularjs代码
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'的上面的代码控制台不会调用里面的兄弟姐妹,它会调用里面只有父母,其中$广播调用里面的兄弟姐妹和父母以及这是性能进入一个行动的地方$ emit是如果您正在使用从小孩到家长的沟通,因为它跳过了一些肮脏的检查。
2.如果第二个控制器是子级,则可以使用子级父级通信
它是最好的方法之一,如果你想做孩子的父母沟通 ,孩子想与直接父母沟通,那么它不需要任何种类的$广播或$发射,但如果你想做从父母到孩子的沟通,那么你必须使用服务或$广播
例如HTML: –
<div ng-controller="ParentCtrl"> <div ng-controller="ChildCtrl"> </div> </div>
Angularjs
app.controller('ParentCtrl', function($scope) { $scope.value='Its parent'; }); app.controller('ChildCtrl', function($scope) { console.log($scope.value); });
每当你使用孩子到家长的沟通,Angularjs会search儿童内部的一个variables,如果它不在里面,那么它会select看到父控制器内的值。
3.使用服务
AngularJS支持使用服务架构的“分离关注”概念。 服务是JavaScript的function,只负责完成一个特定的任务。这使得它们成为一个可维护和可testing的独立实体。服务使用Angularjs的dependency injection机制注入。
Angularjs代码:
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 Hello World和Hello Parent World。 根据服务的angular度文档单例 – 每个依赖于服务的组件获取对服务工厂生成的单个实例的引用 。
4.一些破解 – 在angular.element()的帮助下
这个方法通过它的Id / unique类获取scope()方法的ID / unique.angular.element()方法返回元素和scope()给另一个variables使用$ scopevariables的另一个variables$ scopevariables不是一个好的做法。
HTML: –
<div id='parent' ng-controller='ParentCtrl'>{{varParent}} <span ng-click='getValueFromChild()'>Click to get ValueFormChild</span> <div id='child' ng-controller='childCtrl'>{{varChild}} <span ng-click='getValueFromParent()'>Click to get ValueFormParent </span> </div> </div>
Angularjs: –
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上显示它们自己的值,当你点击文本时,你将在控制台上得到相应的值。如果你点击父控器上的span,浏览器将控制孩子的值,反之亦然。
如果您正在寻找发送和广播事件以跨控制器共享数据或呼叫function ,请查看此链接并通过zbynour
(以最大票数回答)检查答案。 我引用他的答案!
如果firstCtrl的范围是第二个控件范围的父级,那么你的代码应该在firstCtrl中用$ broadcastreplace$ emit:
function firstCtrl($scope){ $scope.$broadcast('someEvent', [1,2,3]); } function secondCtrl($scope){ $scope.$on('someEvent', function(event, mass) {console.log(mass)}); }
如果您的作用域之间没有父子关系,则可以将$ rootScope注入控制器,并将该事件广播到所有子作用域(即secondCtrl)。
function firstCtrl($rootScope){ $rootScope.$broadcast('someEvent', [1,2,3]); }
最后,当你需要从子控制器分派事件到范围向上时,你可以使用$ scope。$ emit。 如果firstCtrl的范围是secondCtrl范围的父级:
function firstCtrl($scope){ $scope.$on('someEvent', function(event, data) { console.log(data); }); } function secondCtrl($scope){ $scope.$emit('someEvent', [1,2,3]); }
两个小提琴:(非服务方式)
1)对于父母控制器 – 使用父控制器的$scope
来发送/广播事件。 http://jsfiddle.net/laan_sachin/jnj6y/
2)在非相关的控制器上使用$rootScope
。 http://jsfiddle.net/VxafF/
实际上使用发射和广播是低效的,因为事件在范围层次上下起伏,容易退化为复杂应用的性能瓶颈。
我会build议使用一项服务。 这是我最近如何在我的一个项目中实现它 – https://gist.github.com/3384419 。
基本理念 – 注册一个pub-sub / event bus作为服务。 然后在需要订阅或发布活动/主题的地方注入该事件总线。
我也知道这种方式。
angular.element($('#__userProfile')).scope().close();
但是我不使用它太多,因为我不喜欢在angular码中使用jQueryselect器。
我不知道这是否超出标准,但是如果你把所有的控制器放在同一个文件中,那么你可以这样做:
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() {...} }]);
正如你所看到的,当调用updateCharts时,指针Ctrl正在调用其他两个控制器的updateChart函数。
有一种方法不依赖于服务, $broadcast
或$emit
。 这并不适用于所有情况,但是如果您有两个相关的控制器可以抽象成指令,那么您可以在指令定义中使用require
选项。 这很可能是ngModel和ngForm的通信方式。 您可以使用它在嵌套的指令控制器或相同的元素之间进行通信。
对于父母/孩子的情况,使用情况如下:
<div parent-directive> <div inner-directive></div> </div>
要实现它的要点:在父指令中,要调用的方法,你应该在this
上定义它们(而不是$scope
):
controller: function($scope) { this.publicMethodOnParentDirective = function() { // Do something } }
在子指令定义上,你可以使用require
选项,这样父控制器就被传递给链接函数(所以你可以从子指令的scope
调用函数。
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=preview
同样使用兄弟指令,但是同一个元素的两个指令:
<div directive1 directive2> </div>
通过在directive1
1上创build一个方法来使用:
controller: function($scope) { this.publicMethod = function() { // Do something } }
而在directive2中,可以通过使用require
选项来调用siblingController,该选项将被传递给链接函数:
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=preview中看到。;
这个的用法?
-
父母:任何情况下,子元素需要与父母“注册”自己。 很像ngModel和ngForm之间的关系。 这些可以添加某些可能影响模型的行为。 你也许也有一些纯粹的基于DOM的东西,父元素需要pipe理某些孩子的位置,比如pipe理或响应滚动。
-
兄弟姐妹:允许指令修改其行为。 ngModel是经典的例子,添加parsing器/validationngModel使用input。
以下是一个不考虑Angular JS的publish-subscribe
方法。
search参数控制器
//Note: Multiple entities publish the same event regionButtonClicked: function () { EM.fireEvent('onSearchParamSelectedEvent', 'region'); }, plantButtonClicked: function () { EM.fireEvent('onSearchParamSelectedEvent', 'plant'); },
searchselect控制器
//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(); } });
事件pipe理器
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); } } } };
全球
var EM = myBase.EventManager;
您可以在您的父控制器(MessageCtrl)中注入'$控制器'服务,然后使用以下方法实例化/注入子控制器(DateCtrl):
$scope.childController = $controller('childController', { $scope: $scope.$new() });
现在,您可以通过调用它的方法来访问您的子控制器的数据,因为这是一项服务。
让我知道如果有任何问题。
在angular1.5中,可以通过执行以下操作来完成:
(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({}); } } }());
这是父组件。 在这里我创build了一个函数,把另一个对象放到我的productForms
数组中 – 注意 – 这只是我的例子,这个函数真的可以是任何东西。
现在我们可以创build另一个将使用require
组件:
(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; }; } }());
在这里,子组件正在创build对父组件函数addNewForm
的引用,然后它可以绑定到HTML并像其他任何函数一样调用。