javascript - eventos - ¿Puede un controlador AngularJS llamar a otro?
eventos en angularjs (13)
¿Es posible que un controlador use otro?
Por ejemplo:
Este documento HTML simplemente imprime un mensaje entregado por el controlador MessageCtrl
en el archivo 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>
El archivo del controlador contiene el siguiente código:
function MessageCtrl()
{
this.message = function() {
return "The current date is: " + new Date().toString();
};
}
Que simplemente imprime la fecha actual;
Si DateCtrl
que agregar otro controlador, DateCtrl
, que DateCtrl
la fecha en un formato específico a MessageCtrl
, ¿cómo se haría esto para hacer esto? El marco DI parece estar preocupado con XmlHttpRequests
y acceder a los servicios.
A continuación se presenta un enfoque de publish-subscribe
que es independiente de Angular JS.
Buscar Param Controller
//Note: Multiple entities publish the same event
regionButtonClicked: function ()
{
EM.fireEvent(''onSearchParamSelectedEvent'', ''region'');
},
plantButtonClicked: function ()
{
EM.fireEvent(''onSearchParamSelectedEvent'', ''plant'');
},
Controlador de opciones de búsqueda
//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();
}
});
Administrador de evento
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;
Aquí hay un ejemplo de una página de dos controladores que comparten datos de servicio:
<!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>
También aquí: https://gist.github.com/3595424
Dos más violines: (enfoque sin servicio)
1) Para el controlador principal - secundario: uso de $scope
del controlador principal para emitir / transmitir eventos. http://jsfiddle.net/laan_sachin/jnj6y/
2) Usando $rootScope
en controladores no relacionados. jsfiddle.net/VxafF
En angular 1.5 esto se puede lograr haciendo lo siguiente:
(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({});
}
}
}());
Este es el componente padre. En esto, he creado una función que inserta otro objeto en mi matriz productForms
- nota: este es solo mi ejemplo, esta función puede ser cualquier cosa realmente.
Ahora podemos crear otro componente que hará uso de 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;
};
}
}());
Aquí, el componente secundario está creando una referencia a la función del componente principal addNewForm
que luego puede vincularse al HTML y llamarse como cualquier otra función.
En realidad, el uso de emitir y transmitir es ineficiente porque el evento aumenta y disminuye la jerarquía del alcance, lo que puede degradarse fácilmente en el rendimiento de una aplicación compleja.
Sugeriría usar un servicio. Aquí es cómo lo implementé recientemente en uno de mis proyectos: https://gist.github.com/3384419 .
Idea básica: registrar un bus de publicación / evento / pub como servicio. Luego, inyecte ese bus de eventos donde sea necesario para suscribirse o publicar eventos / temas.
Existe un método que no depende de los servicios, $broadcast
o $emit
. No es adecuado en todos los casos, pero si tiene 2 controladores relacionados que pueden resumirse en directivas, entonces puede usar la opción require
en la definición de directiva. Esto es muy probablemente como se comunican ngModel y ngForm. Puede usar esto para comunicarse entre los controladores de directivas que están anidados o en el mismo elemento.
Para una situación padre / hijo, el uso sería el siguiente:
<div parent-directive>
<div inner-directive></div>
</div>
Y los puntos principales para que funcione: en la directiva principal, con los métodos a llamar, debe definirlos en this
(no en $scope
):
controller: function($scope) {
this.publicMethodOnParentDirective = function() {
// Do something
}
}
En la definición de la directiva secundaria, puede usar la opción require
para que el controlador principal pase a la función de enlace (para que pueda llamar a las funciones desde el scope
de la directiva secundaria).
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();
}
}
Lo anterior se puede ver en http://plnkr.co/edit/poeq460VmQER8Gl9w8Oz?p=preview
Una directiva de hermanos se usa de manera similar, pero ambas directivas en el mismo elemento:
<div directive1 directive2>
</div>
Utilizado creando un método en directive1
:
controller: function($scope) {
this.publicMethod = function() {
// Do something
}
}
Y en la directiva 2 se puede llamar utilizando la opción require
que da como resultado que siblingController se pase a la función de enlace:
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();
}
}
Esto se puede ver en http://plnkr.co/edit/MUD2snf9zvadfnDXq85w?p=preview .
¿Los usos de esto?
Padre: Cualquier caso en el que los elementos hijos deban "registrarse" con un padre. Al igual que la relación entre ngModel y ngForm. Estos pueden agregar ciertos comportamientos que pueden afectar a los modelos. También puede tener algo basado puramente en DOM, donde un elemento padre necesita administrar las posiciones de ciertos hijos, por ejemplo, administrar o reaccionar al desplazamiento.
Hermano: permitir que una directiva modifique su comportamiento. ngModel es el caso clásico, para agregar analizadores / validación al uso de ngModel en las entradas.
Hay múltiples maneras de comunicarse entre los controladores.
El mejor es probablemente compartir un servicio:
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
}
Otra forma es emitiendo un evento en alcance:
function FirstController($scope)
{
$scope.$on(''someEvent'', function(event, args) {});
// another controller or even directive
}
function SecondController($scope)
{
$scope.$emit(''someEvent'', args);
}
En ambos casos, también puede comunicarse con cualquier directiva.
No sé si esto está fuera de los estándares pero si tiene todos sus controladores en el mismo archivo, entonces puede hacer algo como esto:
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() {...}
}]);
Como puede ver, indicadoresCtrl está llamando a las funciones updateChart de los otros dos controladores al llamar a updateCharts.
Puede inyectar el servicio ''$ controller'' en su controlador principal (MessageCtrl) y luego crear una instancia / inyectar el controlador secundario (DateCtrl) usando:
$scope.childController = $controller(''childController'', { $scope: $scope.$new() });
Ahora puede acceder a los datos de su controlador secundario llamando a sus métodos, ya que es un servicio.
Déjame saber si hay algún problema.
Si desea llamar a un controlador a otro, hay cuatro métodos disponibles
- $ rootScope. $ emit () y $ rootScope. $ broadcast ()
- Si el segundo controlador es secundario, puede usar la comunicación padre-hijo.
- Servicios de uso
- Tipo de hack - con la ayuda de angular.element ()
1. $ rootScope. $ Emit () y $ rootScope. $ Broadcast ()
El controlador y su alcance pueden ser destruidos, pero $ rootScope permanece en la aplicación, por eso estamos tomando $ rootScope porque $ rootScope es el padre de todos los ámbitos.
Si está realizando una comunicación de padres a hijos e incluso un niño quiere comunicarse con sus hermanos, puede usar $ broadcast
Si está realizando la comunicación de un niño a otro, no hay hermanos involucrados, entonces puede usar $ 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>
Código 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'');
});
});
En la consola de código anterior de $ emit ''childEmit'' no llamará a los hermanos menores y llamará solo a los padres, donde $ broadcast se llamará a los hermanos ya los padres también. Este es el lugar donde el rendimiento entra en acción. $ Emit es Preferiblemente, si está utilizando la comunicación de hijo a padre porque omite algunos cheques sucios.
2. Si el segundo controlador es secundario, puede usar la comunicación entre padres e hijos.
Es uno de los mejores métodos. Si desea hacer una comunicación padre-hijo con la que el niño quiere comunicarse con el padre inmediato, entonces no necesitaría ningún tipo de $ broadcast o $ emit, pero si quiere hacer una comunicación de padre a hijo, entonces tiene que usar cualquiera de los servicios o $ broadcast
Por ejemplo 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);
});
Cuando esté utilizando la comunicación hijo a padre, Angularjs buscará una variable dentro del niño. Si no está presente en el interior, elegirá ver los valores dentro del controlador principal.
3.Utilizar servicios
AngularJS admite los conceptos de "Separación de problemas" mediante la arquitectura de servicios. Los servicios son funciones de javascript y son responsables de realizar solo tareas específicas. Esto los convierte en una entidad individual que es mantenible y comprobable. Los servicios que se utilizan para inyectar utilizan el Mecanismo de Inyección de Dependencia de Angularjs.
Código 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");
});
Se dará salida Hello Child World y Hello Parent World. De acuerdo con los documentos de servicios de Angular Singletons: cada componente que depende de un servicio obtiene una referencia a la instancia única generada por la fábrica de servicios .
4.Kind of hack - con la ayuda de angular.element ()
Este método obtiene el ámbito () del elemento por su método Id / unique class.angular.element () devuelve el elemento y scope () da $ variable de alcance de otra variable utilizando $ variable de alcance de un controlador dentro de otro no es una buena práctica.
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);
}
});
En el código anterior, los controladores muestran su propio valor en Html y, cuando haga clic en el texto, obtendrá los valores en la consola en consecuencia. Si hace clic en el intervalo de los controladores principales, el navegador mostrará el valor de child y viceversa.
También sé de esta manera.
angular.element($(''#__userProfile'')).scope().close();
Pero no lo uso demasiado, porque no me gusta usar los selectores jQuery en el código angular.
Vea este violín: http://jsfiddle.net/simpulton/XqDxG/
También vea el siguiente video: Comunicación entre controladores
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''];
Si está buscando emitir y transmitir eventos para compartir datos o llamar a funciones entre los controladores , consulte este link : y verifique la respuesta con zbynour
(responda con el máximo de votos). Estoy citando su respuesta!
Si el ámbito de firstCtrl es el padre del ámbito de secondCtrl, su código debería funcionar reemplazando $ emit por $ broadcast en firstCtrl:
function firstCtrl($scope){
$scope.$broadcast(''someEvent'', [1,2,3]);
}
function secondCtrl($scope){
$scope.$on(''someEvent'', function(event, mass) {console.log(mass)});
}
En caso de que no haya una relación padre-hijo entre sus ámbitos, puede inyectar $ rootScope en el controlador y transmitir el evento a todos los ámbitos secundarios (es decir, también secondCtrl).
function firstCtrl($rootScope){
$rootScope.$broadcast(''someEvent'', [1,2,3]);
}
Finalmente, cuando necesite enviar el evento desde el controlador secundario a los ámbitos ascendentes, puede usar $ scope. $ Emit. Si el ámbito de firstCtrl es el padre del ámbito de secondCtrl:
function firstCtrl($scope){
$scope.$on(''someEvent'', function(event, data) { console.log(data); });
}
function secondCtrl($scope){
$scope.$emit(''someEvent'', [1,2,3]);
}