Может ли один контроллер AngularJS вызвать другой?
возможно ли, чтобы один контроллер использовал другой?
например:
этот HTML-документ просто печатает сообщение доставлено .
<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 и доступ к услугам.
13 ответов:
существует несколько способов связи между контроллерами.
лучший из них, вероятно, делится услугой:
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'];
вот одностраничный пример двух контроллеров обмена данными службы:
<!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 ()
- если второй контроллер является дочерним, вы можете использовать родительскую дочернюю связь .
- Использовать Услуги
- вид взлома - с помощью угловых.элемент()
1. $rootScope.$emit () и $rootScope.$broadcast ()
контроллер и его область могут быть уничтожены, но $rootScope остается в приложении, поэтому мы берем $rootScope, потому что $rootScope является родителем всех областей.
если вы выполняете связь от родителя к ребенку и даже ребенок хочет общаться со своими братьями и сестрами, вы можете использовать $broadcast
если вы выполняете связь от ребенка к родителю, нет братьев и сестер invovled, то вы можете используйте $rootScope.$излучают
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' не будет вызывать внутри дочерних братьев и сестер, и он будет вызывать внутри только родителя, где $broadcast вызывается внутри братьев и сестер и родителя.Это место, где приходят в действие.$emit является предпочтительным, если вы используете дочернюю связь с родительской, потому что она пропускает некоторые грязные проверки.
2. Если Второй контроллер является дочерним, вы можете использовать дочернюю родительскую связь
это один из лучших методов, Если вы хотите сделать дочерняя родительская связь где ребенок хочет общаться с непосредственного родителя тогда он не будет нуждаться в каких-либо $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.Пользуйтесь Услугами
AngularJS поддерживает концепции "разделение проблем" использование архитектуры сервисов. Сервисы-это функции javascript и отвечают только за выполнение конкретных задач.Это делает их физ. лицо что это ремонтопригодный и проверяемый.Службы, используемые для инъекции с использованием инъекции зависимостей mecahnism of 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"); });это даст выход Здравствуй, детский мир и привет родительского мира . По угловым документам услуг Singletons-каждый компонент, зависящий от службы, получает ссылку на один созданный экземпляр на сервисной фабрике.
4.Вид взлома-с помощью углового.элемент()
этот метод получает scope () от элемента по его Id / уникальному классу.угловатый.метод 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 и viceversa.
если вы хотите излучать и транслировать события для обмена данными или вызова функций между контроллерами, пожалуйста, посмотрите на это ссылке: и проверьте ответ на
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.$излучать. Если область 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/
на самом деле использование emit и broadcast неэффективно, потому что событие пузырится вверх и вниз по иерархии областей, которая может легко деградировать в производительность bottlement для сложного приложения.
Я бы предложил использовать сервис. Вот как я недавно реализовал его в одном из своих проектов -https://gist.github.com/3384419.
основная идея-Регистрация шины pub-sub / event в качестве услуги. Затем введите эту шину событий, где вам нужно подписаться или публикация событий / тем.
Я тоже знаю об этом способе.
angular.element($('#__userProfile')).scope().close();но я не использую его слишком много, потому что мне не нравится использовать селекторы jQuery в угловом коде.
Я не знаю, если это не соответствует стандартам, но если у вас есть все ваши контроллеры в одном файле, то вы можете сделать что-то вроде этого:
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() {...} }]);как вы можете видеть indicatorsCtrl вызывает функции updateChart других обоих контроллеров при вызове updateCharts.
существует метод, не зависящий от услуг,
$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опция, поэтому родительский контроллер передается в функцию link (поэтому вы можете вызывать функции на нем из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 } }и в 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, где родительский элемент должен управлять позициями определенных детей, скажем, управлять или реагировать прокрутка.
Sibling: позволяет директиве изменять свое поведение. ngModel-это классический случай, чтобы добавить Парсеры / проверку для использования ngModel на входах.
вы можете ввести службу "$controller " в родительский контроллер (MessageCtrl), а затем создать экземпляр/ввести дочерний контроллер(DateCtrl) с помощью:
$scope.childController = $controller('childController', { $scope: $scope.$new() });теперь вы можете получить доступ к данным из вашего дочернего контроллера, вызвав его методы, поскольку это служба.
Дайте мне знать, если любой вопрос.
ниже
publish-subscribeподход, который не зависит от углового JS.Поиск Param Controller
//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;
в 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({}); } } }());это родительский компонент. В этом я создал функцию, которая толкает другой объект в мой
productFormsarray-note-это просто мой пример, эта функция может быть чем угодно на самом деле.теперь мы можем создать еще один компонент, который будет использовать
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 и вызвать как любую другую функцию.
Comments