La noche de ayer me quedé en la oficina terminando de corregir algunos aspectos de una aplicación. Una aplicación móvil híbrida con
AngularJS,
Angular UI Router y
Bootstrap. El código estaba bien, pero uno de mis controladores estaba gatillando el error
Unknown service provider.
Angular UI Router permite conceptualizar una aplicación web como una máquina de estados.
Cada acción que se realiza puede ser un estado separado, y cada estado, desde el enfoque de desarrollo que estamos adoptando, tiene sus propias vistas.
A su vez, cada vista esta manejada por su propio controlador, y eventualmente antes de cargarse puede requerir que resuelva determinada petición (
resolve).
Ejemplificando con código:
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider
.when('/', '/login')
.otherwise('/');
$stateProvider
.state('login', {
url: '/login'
, views: {
'login': {
templateUrl: 'include/login.include.html'
, controller: 'loginCtrl'
}
}
, onEnter: function() {
loggedin = false;
localStorage.setItem('loggeduser', null);
}
})
.state('plan', {
url: '/plan'
, views: {
'menu': {
templateUrl: 'include/menu.include.html'
, controller: 'menuCtrl'
}
, 'plan': {
templateUrl: 'include/plan.include.html'
, controller: 'planCtrl'
}
, 'client': {
templateUrl: 'include/client.include.html'
, controller: 'clientCtrl'
, resolve: {
clients: ['pouchWrapper', function(pouchWrapper){
var cliMap = function(doc){
if(doc.dbtable === 'cli'){
emit(doc, null);
}
}
// Rescata el listado de clientes desde un repositorio local
return pouchWrapper.retrieveList(cliMap);
}]
}
} }
, onEnter: function($location) {
if(!loggedin){
console.log('No ha ingresado al sistema');
$location.path('/logout');
}
}
}) ;
});
Si se dan cuenta algunas plantillas (
template) tienen indicado cual es su correspondiente controlador (
controller).
Y en el controlador
clientCtrl
app.controller('clientCtrl', ['$scope', '$location', 'clients',
function($scope, $location, clients){
// ... aqui va mucho código
}]);
El problema es que estando bien codificada la máquina de estados, y cargando cada vista como correspondía, AngularJS reportaba el error
Unknown service provider para
clients, sin indeicar origen del error ni líneas de referencia
(es una maravilla depurar código a ciegas :-/ ).
Después de mucho buscar y leer en la
Wiki y sección de
Issues del
Github de Angular UI Router, llegué a un post en StackOverflow
(lo siento, perdí el enlace original), donde explicaban que
el error se genera cuando el estado tiene el controlador referenciado tanto en la configuración como dentro de la plantilla.
En la plantilla
include/client.include.html
<div data-ng-controller="clientCtrl">
<!-- ... aquí va el contenido de la plantilla para el bloque client -->
</div>
El error lo he destacado en
negrita, el controlador o está referenciado en la configuración del estado, o está referenciado dentro de la plantilla, pero
NO en ambos.
Después de ver la solución, y corregir el problema
(eliminando la referencia al controlador en la plantilla) es entendible lo que está sucediendo. Si recuerdan, el controlador recibe como tercer parámetro
clients (que es lo indicamos en el atributo
resolve del estado, que queremos resolver antes de desplegar) . Al poner la referencia directamente en la plantilla,
clients queda vacío, ergo
Unknown service provider.