AngularJS: ¿Cómo puedo pasar variables entre controladores?
Tengo dos controladores Angular:
function Ctrl1($scope) {
$scope.prop1 = "First";
}
function Ctrl2($scope) {
$scope.prop2 = "Second";
$scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}
No puedo usarlo Ctrl1
dentro Ctrl2
porque no está definido. Sin embargo, si trato de pasarlo así...
function Ctrl2($scope, Ctrl1) {
$scope.prop2 = "Second";
$scope.both = Ctrl1.prop1 + $scope.prop2; //This is what I would like to do ideally
}
Recibo un error. ¿Alguien sabe como hacer esto?
Haciendo
Ctrl2.prototype = new Ctrl1();
También falla.
NOTA: Estos controladores no están anidados uno dentro del otro.
Una forma de compartir variables entre varios controladores es crear un servicio e inyectarlo en cualquier controlador donde desee usarlo.
Ejemplo de servicio sencillo:
angular.module('myApp', [])
.service('sharedProperties', function () {
var property = 'First';
return {
getProperty: function () {
return property;
},
setProperty: function(value) {
property = value;
}
};
});
Usando el servicio en un controlador:
function Ctrl2($scope, sharedProperties) {
$scope.prop2 = "Second";
$scope.both = sharedProperties.getProperty() + $scope.prop2;
}
Esto se describe muy bien en este blog (Lección 2 en adelante en particular).
Descubrí que si desea vincular estas propiedades a través de múltiples controladores, funciona mejor si se vincula a la propiedad de un objeto en lugar de a un tipo primitivo (booleano, cadena, número) para conservar la referencia vinculada.
Ejemplo: var property = { Property1: 'First' };
en lugar de var property = 'First';
.
ACTUALIZACIÓN: Para (con suerte) aclarar las cosas, aquí hay un violín que muestra un ejemplo de:
- Vinculación a copias estáticas del valor compartido (en myController1)
- Vinculación a una primitiva (cadena)
- Vinculación a la propiedad de un objeto (guardada en una variable de alcance)
- Vinculación a valores compartidos que actualizan la interfaz de usuario a medida que se actualizan los valores (en myController2)
- Vinculación a una función que devuelve una primitiva (cadena)
- Vinculación a la propiedad del objeto.
- Enlace bidireccional a la propiedad de un objeto.
Me gusta ilustrar cosas simples con ejemplos simples :)
Aquí hay un Service
ejemplo muy simple:
angular.module('toDo',[])
.service('dataService', function() {
// private variable
var _dataObj = {};
// public API
this.dataObj = _dataObj;
})
.controller('One', function($scope, dataService) {
$scope.data = dataService.dataObj;
})
.controller('Two', function($scope, dataService) {
$scope.data = dataService.dataObj;
});
Y aquí el jsbin
Y aquí tienes un Factory
ejemplo muy sencillo:
angular.module('toDo',[])
.factory('dataService', function() {
// private variable
var _dataObj = {};
// public API
return {
dataObj: _dataObj
};
})
.controller('One', function($scope, dataService) {
$scope.data = dataService.dataObj;
})
.controller('Two', function($scope, dataService) {
$scope.data = dataService.dataObj;
});
Y aquí el jsbin
Si esto es demasiado simple, aquí hay un ejemplo más sofisticado.
Consulte también la respuesta aquí para comentarios relacionados con las mejores prácticas.
--- Sé que esta respuesta no es para esta pregunta, pero quiero que las personas que lean esta pregunta y quieran manejar Servicios como Fábricas eviten problemas al hacer esto ----
Para ello necesitarás utilizar un Servicio o una Fábrica.
Los servicios son la MEJOR PRÁCTICA para compartir datos entre controladores no anidados.
Una muy, muy buena anotación sobre este tema sobre el intercambio de datos es cómo declarar objetos. Tuve mala suerte porque caí en una trampa de AngularJS antes de leer sobre ello y me sentí muy frustrado. Así que déjame ayudarte a evitar este problema.
Leí en el "ng-book: El libro completo sobre AngularJS" que los modelos ng de AngularJS que se crean en controladores como datos básicos son INCORRECTOS.
Un elemento $scope debería crearse así:
angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
// best practice, always use a model
$scope.someModel = {
someValue: 'hello computer'
});
Y no así:
angular.module('myApp', [])
.controller('SomeCtrl', function($scope) {
// anti-pattern, bare value
$scope.someBareValue = 'hello computer';
};
});
Esto se debe a que se recomienda (MEJOR PRÁCTICA) que el DOM (documento html) contenga las llamadas como
<div ng-model="someModel.someValue"></div> //NOTICE THE DOT.
Esto es muy útil para los controladores anidados si desea que su controlador secundario pueda cambiar un objeto desde el controlador principal....
Pero en su caso no quiere ámbitos anidados, pero existe un aspecto similar para llevar objetos de los servicios a los controladores.
Digamos que tiene su servicio 'Fábrica' y en el espacio de retorno hay un objetoA que contiene el objetoB que contiene el objetoC.
Si desde su controlador desea OBTENER el objeto C en su alcance, es un error decir:
$scope.neededObjectInController = Factory.objectA.objectB.objectC;
Eso no funcionará... En lugar de eso, usa solo un punto.
$scope.neededObjectInController = Factory.ObjectA;
Luego, en el DOM puedes llamar al objeto C desde el objeto A. Esta es una práctica recomendada relacionada con las fábricas y, lo más importante, ayudará a evitar errores inesperados y no detectables.