一个AngularJS控制器可以调用另一个控制器吗?

是否可以让一个控制器使用另一个控制器?

例如:

这个HTML文档只是打印由messageCtrl.js文件中的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();
};
}

它只是打印当前日期;

如果我要添加另一个控制器,DateCtrl,它以特定的格式将日期交还给MessageCtrl,该如何做呢?DI框架似乎与XmlHttpRequests和访问服务有关。

399770 次浏览

控制器之间有多种通信方式。

最好的可能是共享服务:

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/

请观看以下视频:控制器间通信

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'];

实际上,使用emit和broadcast效率很低,因为事件会在作用域层次结构中上下冒泡,这很容易导致复杂应用程序的性能瓶颈。

我建议使用服务。下面是我最近在我的一个项目https://gist.github.com/3384419中实现它的方法。

基本思想-将发布-订阅/事件总线注册为服务。然后在需要订阅或发布事件/主题的地方注入事件总线。

下面是两个控制器共享服务数据的单页示例:

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

另外两个提琴:(非服务方法)

1)对于父-子控制器-使用父控制器的$scope来发射/广播事件。 # EYZ0 < / p > 2)在不相关的控制器上使用$rootScope。 # EYZ0 < / p >

我也知道这条路。

angular.element($('#__userProfile')).scope().close();

但我不太使用它,因为我不喜欢在angular代码中使用jQuery选择器。

有一个方法不依赖于服务,$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上创建一个方法使用:

controller: function($scope) {
this.publicMethod = function() {
// Do something
}
}

在directive2中,可以通过使用require选项来调用,这将导致siblingController被传递给link函数:

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的输入中添加解析器/验证。

下面是一个与Angular JS无关的publish-subscribe方法。

搜索参数控制器

//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;

如果你正在寻找&广播事件以跨控制器共享数据或调用函数,请看看这个链接:并通过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]);
}

我不知道这是否超出了标准,但如果你把所有的控制器都放在同一个文件上,那么你可以这样做:

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函数。

你可以在你的父控制器(MessageCtrl)中注入'$controller'服务,然后实例化/注入子控制器(DateCtrl)使用 # EYZ0 < / p > 现在你可以通过调用子控制器的方法来访问数据,因为它是一个服务 如果有任何问题请告诉我。

如果你想将一个控制器调用到另一个控制器中,有四种方法可用

  1. $rootScope.$emit()和$rootScope.$broadcast()
  2. 如果第二控制器是子控制器,你可以使用父-子通信。
  3. 使用服务
  4. 有点hack -在angular.element()的帮助下

1. $rootScope.$emit()和$rootScope.$broadcast()

控制器和它的作用域可以被破坏, 但是$rootScope仍然存在于整个应用程序中,这就是为什么我们要使用$rootScope,因为$rootScope是所有作用域的父作用域

如果您正在执行从父母到孩子的通信,甚至孩子也想与它的兄弟姐妹通信,您可以使用$broadcast

如果您正在执行从子节点到父节点的通信,没有涉及到兄弟节点,那么您可以使用$rootScope.$emit

超文本标记语言

<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'不会调用内部的子兄弟姐妹,它只会调用内部的父兄弟姐妹,其中$broadcast也会在内部的兄弟姐妹和父兄弟姐妹中调用。这是表现发挥作用的地方。如果您正在使用子到父通信,$emit是更可取的,因为它跳过了一些脏检查。

2. 如果第二控制器是子控制器,你可以使用子-父通信

这是最好的方法之一,如果你想做亲子沟通,孩子想要与直接父通信,那么它不需要任何类型的$broadcast或$emit,但如果你想做从父母到孩子的通信,那么你必须使用service或$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);
});

当你使用子控制器到父控制器的通信时,Angularjs会在子控制器中搜索一个变量,如果它不在父控制器中,那么它会选择查看父控制器中的值。

3.使用服务

AngularJS使用服务架构支持“业务分离”的概念。服务是javascript函数,只负责执行特定的任务。这使它们成为单独的实体,即可维护和可测试。使用Angularjs的依赖注入机制进行注入的服务。

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文档中的services 单例——依赖于服务的每个组件都获得由服务工厂生成的单个实例的引用

4.有点hack -在angular.element()的帮助下

这个方法通过它的Id / unique class.angular.element()方法从元素中获取scope()返回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和当你将点击文本,你会得到相应的值在控制台。如果你点击父控制器跨度,浏览器将控制台的child值,反之亦然。

在angular 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并像其他函数一样调用。

你可以使用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');
}


}]);