是否可以让一个控制器使用另一个?
例如:
此HTML文档只是打印MessageCtrl
文件中messageCtrl.js
控制器提供的消息。
<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();
};
}
只打印当前日期;
如果我要添加另一个控制器,DateCtrl
以特定格式将日期交回MessageCtrl
,那么怎么会这样做呢? DI框架似乎关注XmlHttpRequests
和访问服务。
答案 0 :(得分:695)
如何在控制器之间进行通信有多种方法。
最好的可能是共享服务:
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);
}
在这两种情况下,您也可以与任何指令进行通信。
答案 1 :(得分:120)
看到这个小提琴:http://jsfiddle.net/simpulton/XqDxG/
另请观看以下视频:Communicating Between Controllers
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'];
答案 2 :(得分:52)
以下是两个控制器共享服务数据的一页示例:
<!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>
答案 3 :(得分:51)
如果您想将一个控制器调用另一个控制器,则可以使用四种方法
<强> 1。 $ rootScope。$ emit()和$ rootScope。$ broadcast()
控制器及其范围可能会被破坏, 但$ rootScope仍然在整个应用程序中,这就是为什么我们采用$ rootScope,因为$ rootScope是所有范围的父级。
如果您正在进行从父母到孩子的沟通,即使孩子想与其兄弟姐妹沟通,您也可以使用$ broadcast
如果您正在从孩子到父母进行沟通,那么没有兄弟姐妹进行过调用,那么您可以使用$ 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&#39; childEmit&#39;的上述代码控制台中不会在子兄弟姐妹内部打电话,它会在内部只调用父母,其中$ broadcast也会在兄弟姐妹和父母之间被调用。这是表演进入行动的地方。如果你使用孩子与父母的沟通,$ emit是首选的因为它会跳过一些脏的支票。
<强> 2。如果第二个控制器是孩子,您可以使用子父母沟通
它是最好的方法之一,如果你想做孩子父母沟通,孩子想要与直接父母沟通那么它就不需要任何种类的广播或$ emit但是如果你想从父母到孩子进行沟通,那么你必须使用服务或$ broadcast
例如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);
});
每当你使用child to parent通信时,Angularjs将搜索子内部的变量,如果内部不存在,那么它将选择查看父控制器内的值。
3.使用服务
AngularJS使用服务架构支持&#34;关注分离&#34; 的概念。服务是javascript函数,仅负责执行特定任务。这使得它们成为个体实体,可维护且可测试。服务使用Angularjs的依赖注入mecahnism注入服务
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 Child World和Hello Parent World。根据Angular docs of services Singletons - 依赖于服务的每个组件都获得对服务工厂生成的单个实例的引用。
4.黑客攻击 - 在angular.element()
的帮助下
此方法通过其Id /唯一class从元素获取scope()。angular.element()方法返回元素,scope()使用另一个变量的$ scope变量使用另一个变量的$ scope变量而不是良好的做法。
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,浏览器将控制child的值,反之亦然。
答案 4 :(得分:32)
如果你想发射&amp;广播事件以跨控制器共享数据或呼叫功能,请查看此link:并通过zbynour
检查答案(以最大投票回答)。我在引用他的答案!!!
如果firstCtrl的范围是secondCtrl范围的父级,则代码应该通过在firstCtrl中用$ broadcast替换$ 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]);
}
答案 5 :(得分:24)
另外两个小提琴:(非服务方式)
1)对于Parent-Child控制器 - 使用父控制器的$scope
发出/广播事件。
http://jsfiddle.net/laan_sachin/jnj6y/
2)在非相关控制器上使用$rootScope
。
http://jsfiddle.net/VxafF/
答案 6 :(得分:16)
实际上使用发射和广播是低效的,因为事件在范围层次结构中上下波动,这很容易降级为复杂应用程序的性能瓶装。
我建议使用服务。以下是我最近在我的一个项目https://gist.github.com/3384419中实现它的方法。
基本思路 - 将pub-sub / event总线注册为服务。然后在需要订阅或发布事件/主题的地方注入该事件总线。
答案 7 :(得分:3)
我也知道这种方式。
angular.element($('#__userProfile')).scope().close();
但是我没有太多使用它,因为我不喜欢在角度代码中使用jQuery选择器。
答案 8 :(得分:3)
我不知道这是否超出标准,但如果你的所有控制器都在同一个文件上,那么你可以这样做:
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时,indicatorsCtrl正在调用其他两个控制器的updateChart函数。
答案 9 :(得分:2)
有一种方法不依赖于服务,$broadcast
或$emit
。它并不适用于所有情况,但如果您有2个相关的控制器可以抽象为指令,那么您可以在指令定义中使用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
:
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的东西,其中父元素需要管理某些孩子的位置,比如管理或反应滚动。
兄弟姐妹:允许指令修改其行为。 ngModel是经典案例,用于在输入上添加解析器/验证到ngModel。
答案 10 :(得分:2)
您可以在父控制器(MessageCtrl)中注入'$ controller'服务,然后使用以下方法实例化/注入子控制器(DateCtrl):
$scope.childController = $controller('childController', { $scope: $scope.$new() });
现在,您可以通过调用其方法来访问子控制器中的数据,因为它是一项服务 如果有任何问题,请告诉我。
答案 11 :(得分:1)
以下是publish-subscribe
方法,与Angular JS无关。
搜索参数控制器
//Note: Multiple entities publish the same event
regionButtonClicked: function ()
{
EM.fireEvent('onSearchParamSelectedEvent', 'region');
},
plantButtonClicked: function ()
{
EM.fireEvent('onSearchParamSelectedEvent', 'plant');
},
搜索选择控制器
//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();
}
});
活动经理
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;
答案 12 :(得分:1)
在角度1.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({});
}
}
}());
这是父组件。在这里我创建了一个函数,将另一个对象推入我的productForms
数组 - 注意 - 这只是我的例子,这个函数可以是真正的。
现在我们可以创建另一个将使用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;
};
}
}());
这里,子组件正在创建对父组件函数addNewForm
的引用,然后可以将其绑定到HTML并像其他任何函数一样进行调用。
答案 13 :(得分:0)
您可以使用AngularJS提供的$controller
服务。
angular.module('app',[]).controller('DateCtrl', ['$scope', function($scope){
$scope.currentDate = function(){
return "The current date is: " + new Date().toString();
}
}]);
angular.module('app').controller('MessageCtrl', ['$scope', function($scope){
angular.extend(this, $controller('DateCtrl', {
$scope: $scope
}));
$scope.messageWithDate = function(message){
return "'"+ message + "', " + $scope.currentDate;
}
$scope.action2 = function(){
console.log('Overridden in ChildCtrl action2');
}
}]);