jueves, junio 05, 2014

AngularJS UI Router, resolve & Unknown service provider ERROR

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.




Publicar un comentario