Может ли один контроллер вызвать другого?
Возможно ли, что один контроллер использует другой?
Например:
Этот документ 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
и доступом к службам.
Ответы
Ответ 1
Существует несколько способов взаимодействия между контроллерами.
Лучшим из них является, возможно, совместное использование сервиса:
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);
}
В обоих случаях вы также можете общаться с любой директивой.
Ответ 2
Смотрите эту скрипту: 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'];
Ответ 3
Ниже приведен пример одной страницы двух контроллеров, которые используют данные службы:
<!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
Ответ 4
Если вы хотите вызвать один контроллер в другой, есть четыре доступных метода
- $rootScope. $emit() и $rootScope. $broadcast()
- Если второй контроллер является дочерним, вы можете использовать связь с родительским ребенком.
- Использовать службы
- Вид взлома - с помощью angular.element()
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>
Код углового кода
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. Если второй контроллер является дочерним, вы можете использовать Child Parent
Его один из лучших методов. Если вы хотите сделать родительскую связь с родителями, где ребенок хочет общаться с непосредственным родителем, тогда ему не понадобится какой-либо вид $broadcast или $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);
});
Всякий раз, когда вы используете связь между родителями и родителями, Angularjs будет искать переменную внутри дочернего элемента. Если ее нет внутри, она будет выбирать значения внутри родительского контроллера.
Службы 3.Use
AngularJS поддерживает концепции "Разделение проблем" с использованием архитектуры сервисов. Службы являются функциями javascript и несут ответственность за выполнение только определенных задач. Это делает их индивидуальной сущностью поддерживаемой и проверяемой. Службы, используемые для инъекций с использованием метода Mackahnism Dependency Injection для 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 документам сервисов Singletons - каждый компонент, зависящий от службы, получает ссылку на единственный экземпляр, сгенерированный службой factory.
4.Kack of hack - с помощью angular.element()
Этот метод получает scope() от элемента методом Id/unique class.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, и когда вы нажмете на текст, вы соответственно получите значения в консоли. Если вы нажмете на родительский контроллер, браузер будет передавать значение дочернего элемента и наоборот.
Ответ 5
Если вы хотите генерировать и транслировать события для обмена данными или функциями вызова через контроллеры, посмотрите на ссылку : и проверьте ответ zbynour
(ответ с максимальным количеством голосов). Я цитирую его ответ!!!
Если область firstCtrl является родительской для области secondCtrl, ваш код должен работать, заменив $emit на $broadcast в firstCtrl:
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]);
}
Ответ 6
Еще две скрипки: (подход без обслуживания)
1) Для контроллера Parent-Child - использование $scope
родительского контроллера для генерации/трансляции событий. http://jsfiddle.net/laan_sachin/jnj6y/
2) Используя $rootScope
для несвязанных контроллеров. http://jsfiddle.net/VxafF/
Ответ 7
Фактически использование emit и broadcast неэффективно, потому что событие пузырится вверх и вниз по иерархии областей, которые могут легко деградировать в пакет производительности для сложного приложения.
Я бы предложил использовать сервис. Вот как я недавно реализовал его в одном из моих проектов - https://gist.github.com/3384419.
Основная идея - зарегистрировать шину pub-sub/event в качестве сервиса. Затем введите эту шину событий, когда вам нужно подписаться или опубликовать события/темы.
Ответ 8
Я также знаю об этом.
angular.element($('#__userProfile')).scope().close();
Но я не слишком сильно его использую, потому что мне не нравится использовать селектора jQuery в коде angular.
Ответ 9
Я не знаю, если это не соответствует стандартам, но если у вас есть все ваши контроллеры в одном файле, вы можете сделать что-то вроде этого:
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() {...}
}]);
Как вы можете видеть, индикаторыCtrl вызывает функции updateChart других обоих контроллеров при вызове updateCharts.
Ответ 10
Существует метод, не зависящий от служб, $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
Аналогично используется директива sibling, но обе директивы одного элемента:
<div directive1 directive2>
</div>
Используется при создании метода на directive1
:
controller: function($scope) {
this.publicMethod = function() {
// Do something
}
}
И в директиве2 это можно вызвать с помощью опции 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, где родительский элемент должен управлять позициями определенных детей, например, управлять или реагировать на прокрутку.
-
Sibling: позволяет директиве изменять свое поведение. ngModel - классический случай, чтобы добавить парсеров/валидацию к использованию ngModel на входах.
Ответ 11
Ниже приведен подход 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);
}
}
}
};
Global
var EM = myBase.EventManager;
Ответ 12
Вы можете ввести службу $controller в свой родительский контроллер (MessageCtrl), а затем создать/ввести дочерний контроллер (DateCtrl), используя:
$scope.childController = $controller('childController', { $scope: $scope.$new() });
Теперь вы можете получить доступ к данным из вашего дочернего контроллера, вызвав его методы, поскольку это служба.
Дайте мне знать, если возникнут какие-либо проблемы.
Ответ 13
В 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 и называется как любая другая функция.