javascriptangularjsangular-ui-routerhottowel

Initialization of shared data service in AngularJS fails


I'm pretty new to AngularJS and I want to build an application which complies to the AngularJS style guide by John Papa. To get a good understanding about this practices I'm using the HotTowel skeleton.

In my application I want to consume an HTTP API endpoint which fetches information about the authenticated user. Lets call it example.com/api/users/me. This data should be used by many controllers so what I need here is a shared data service. An important requirement is that the API should not be called twice. For this reason I implemented an method to initialize the service once with an API call. I used the core/dataservice.js from HotTowel as a reference and implemented my service like this:

//File: currentUserDataService.js
(function() {
    'use strict';

    angular
        .module('app.core')
        .factory('currentUserDataService', currentUserDataService);

    currentUserDataService.$inject = ['$http', '$q', 'exception', 'logger', 'config'];
    /* @ngInject */
    function currentUserDataService($http, $q, exception, logger, config) {
        var user = {};

        var service = {
            init: init,
            getData: getData
        };

        return service;

        function getData(){
            return user;
        }

        function init() {
            return $http.get(config.apiBaseUrl + '/users/me')
                .then(success)
                .catch(fail);

            function success(response) {
                console.log(response.data);
                user = response.data.data;
            }

            function fail(e) {
                console.log(e);
                return exception.catcher('XHR Failed for getPeople')(e);
            }
        }
    }
})();

Now I want to consume this service in the existing controllers DashboardController and ShellController. My first step is to configure the dashboard route to resolve the promise of my service:

//File: dashboard.route.js
(function() {
    'use strict';

    angular
        .module('app.dashboard')
        .run(appRun);

    appRun.$inject = ['routerHelper','currentUserDataService'];
    /* @ngInject */
    function appRun(routerHelper,currentUserDataService) {
        routerHelper.configureStates(getStates(currentUserDataService));
    }

    function getStates(currentUserDataService) {
        return [
            {
                state: 'dashboard',
                config: {
                    url: '/',
                    templateUrl: 'app/dashboard/dashboard.html',
                    controller: 'DashboardController',
                    controllerAs: 'vm',
                    title: 'dashboard',
                    settings: {
                        nav: 1,
                        content: '<i class="fa fa-dashboard"></i> Dashboard'
                    },
                    resolve: {
                        'currentUserDataService': function(currentUserDataService){
                            return currentUserDataService.init;
                        }
                    }
                }
            }
        ];
    }
})();

In my understanding I should be able now to retrieve data using the getData function of the service from my controller:

//File dashboad.controller.js
(function() {
    'use strict';

    angular
        .module('app.dashboard')
        .controller('DashboardController', DashboardController);

    DashboardController.$inject = ['$q', 'currentUserDataService', 'logger'];
    /* @ngInject */
    function DashboardController($q, currentUserDataService, logger) {
        var vm = this;
        vm.user = {};
        vm.title = 'Dashboard';
        vm.getFullName = getFullName;

        activate();

        function activate() {
            getCurrentUser();
            logger.info('Activated Dashboard View');
        }

        function getCurrentUser() {
            console.log(currentUserDataService);
            //Interestingly I only get the init() function logged on the console
            vm.user = currentUserDataService.getData(); //It fails here
            console.log(vm.user);
            return vm.user;
        }
        function getFullName(){
            return vm.user.name + ' ' + vm.user.lastName;
        }
    }
})();

When I try to run the application I get the following error

Error: currentUserDataService.getData is not a function
getCurrentUser@http://localhost:3000/src/client/app/dashboard/dashboard.controller.js:33:14
activate@http://localhost:3000/src/client/app/dashboard/dashboard.controller.js:24:4
DashboardController@http://localhost:3000/src/client/app/dashboard/dashboard.controller.js:21:3
instantiate@http://localhost:3000/bower_components/angular/angular.js:4640:14
$controller@http://localhost:3000/bower_components/angular/angular.js:10042:18
$ViewDirectiveFill/<.compile/<@http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:4081:28
invokeLinkFn@http://localhost:3000/bower_components/angular/angular.js:9623:9
nodeLinkFn@http://localhost:3000/bower_components/angular/angular.js:9022:11
compositeLinkFn@http://localhost:3000/bower_components/angular/angular.js:8333:13
publicLinkFn@http://localhost:3000/bower_components/angular/angular.js:8213:30
lazyCompilation@http://localhost:3000/bower_components/angular/angular.js:8551:16
updateView@http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:4021:23
$ViewDirective/directive.compile/</<@http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:3959:11
$RootScopeProvider/this.$get</Scope.prototype.$broadcast@http://localhost:3000/bower_components/angular/angular.js:17348:15
transitionTo/$state.transition<@http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:3352:11
processQueue@http://localhost:3000/bower_components/angular/angular.js:15757:28
scheduleProcessQueue/<@http://localhost:3000/bower_components/angular/angular.js:15773:27
$RootScopeProvider/this.$get</Scope.prototype.$eval@http://localhost:3000/bower_components/angular/angular.js:17025:16
$RootScopeProvider/this.$get</Scope.prototype.$digest@http://localhost:3000/bower_components/angular/angular.js:16841:15
$RootScopeProvider/this.$get</Scope.prototype.$apply@http://localhost:3000/bower_components/angular/angular.js:17133:13
done@http://localhost:3000/bower_components/angular/angular.js:11454:36
completeRequest@http://localhost:3000/bower_components/angular/angular.js:11652:7
requestLoaded@http://localhost:3000/bower_components/angular/angular.js:11593:9
EventHandlerNonNull*createHttpBackend/<@http://localhost:3000/bower_components/angular/angular.js:11576:7
sendReq@http://localhost:3000/bower_components/angular/angular.js:11423:9
$http/serverRequest@http://localhost:3000/bower_components/angular/angular.js:11133:16
processQueue@http://localhost:3000/bower_components/angular/angular.js:15757:28
scheduleProcessQueue/<@http://localhost:3000/bower_components/angular/angular.js:15773:27
$RootScopeProvider/this.$get</Scope.prototype.$eval@http://localhost:3000/bower_components/angular/angular.js:17025:16
$RootScopeProvider/this.$get</Scope.prototype.$digest@http://localhost:3000/bower_components/angular/angular.js:16841:15
$RootScopeProvider/this.$get</Scope.prototype.$apply@http://localhost:3000/bower_components/angular/angular.js:17133:13
bootstrapApply@http://localhost:3000/bower_components/angular/angular.js:1713:9
invoke@http://localhost:3000/bower_components/angular/angular.js:4625:16
bootstrap/doBootstrap@http://localhost:3000/bower_components/angular/angular.js:1711:5
bootstrap@http://localhost:3000/bower_components/angular/angular.js:1731:12
angularInit@http://localhost:3000/bower_components/angular/angular.js:1616:5
@http://localhost:3000/bower_components/angular/angular.js:30709:5
jQuery.Callbacks/fire@http://localhost:3000/bower_components/jquery/dist/jquery.js:3187:11
jQuery.Callbacks/self.fireWith@http://localhost:3000/bower_components/jquery/dist/jquery.js:3317:7
.ready@http://localhost:3000/bower_components/jquery/dist/jquery.js:3536:3
completed@http://localhost:3000/bower_components/jquery/dist/jquery.js:3552:2
EventListener.handleEvent*jQuery.ready.promise@http://localhost:3000/bower_components/jquery/dist/jquery.js:3573:4
@http://localhost:3000/bower_components/jquery/dist/jquery.js:3583:1
@http://localhost:3000/bower_components/jquery/dist/jquery.js:34:3
@http://localhost:3000/bower_components/jquery/dist/jquery.js:15:2
 <div ui-view="" class="shuffle-animation ng-scope">

It seems that the object returned from my service doesn't include other methods than init. What is wrong with my code?

//EDIT: Tried the answer from @DanEEStar but now there's another error:

Error: [ ] currentUserDataService is undefined
getCurrentUser@http://localhost:3000/src/client/app/dashboard/dashboard.controller.js:33:4
activate@http://localhost:3000/src/client/app/dashboard/dashboard.controller.js:24:4
DashboardController@http://localhost:3000/src/client/app/dashboard/dashboard.controller.js:21:3
instantiate@http://localhost:3000/bower_components/angular/angular.js:4640:14
$controller@http://localhost:3000/bower_components/angular/angular.js:10042:18
$ViewDirectiveFill/<.compile/<@http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:4081:28
invokeLinkFn@http://localhost:3000/bower_components/angular/angular.js:9623:9
nodeLinkFn@http://localhost:3000/bower_components/angular/angular.js:9022:11
compositeLinkFn@http://localhost:3000/bower_components/angular/angular.js:8333:13
publicLinkFn@http://localhost:3000/bower_components/angular/angular.js:8213:30
lazyCompilation@http://localhost:3000/bower_components/angular/angular.js:8551:16
updateView@http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:4021:23
$ViewDirective/directive.compile/</<@http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:3959:11
$RootScopeProvider/this.$get</Scope.prototype.$broadcast@http://localhost:3000/bower_components/angular/angular.js:17348:15
transitionTo/$state.transition<@http://localhost:3000/bower_components/angular-ui-router/release/angular-ui-router.js:3352:11
processQueue@http://localhost:3000/bower_components/angular/angular.js:15757:28
scheduleProcessQueue/<@http://localhost:3000/bower_components/angular/angular.js:15773:27
$RootScopeProvider/this.$get</Scope.prototype.$eval@http://localhost:3000/bower_components/angular/angular.js:17025:16
$RootScopeProvider/this.$get</Scope.prototype.$digest@http://localhost:3000/bower_components/angular/angular.js:16841:15
$RootScopeProvider/this.$get</Scope.prototype.$apply@http://localhost:3000/bower_components/angular/angular.js:17133:13
done@http://localhost:3000/bower_components/angular/angular.js:11454:36
completeRequest@http://localhost:3000/bower_components/angular/angular.js:11652:7
requestLoaded@http://localhost:3000/bower_components/angular/angular.js:11593:9
EventHandlerNonNull*createHttpBackend/<@http://localhost:3000/bower_components/angular/angular.js:11576:7
sendReq@http://localhost:3000/bower_components/angular/angular.js:11423:9
$http/serverRequest@http://localhost:3000/bower_components/angular/angular.js:11133:16
processQueue@http://localhost:3000/bower_components/angular/angular.js:15757:28
scheduleProcessQueue/<@http://localhost:3000/bower_components/angular/angular.js:15773:27
$RootScopeProvider/this.$get</Scope.prototype.$eval@http://localhost:3000/bower_components/angular/angular.js:17025:16
$RootScopeProvider/this.$get</Scope.prototype.$digest@http://localhost:3000/bower_components/angular/angular.js:16841:15
$RootScopeProvider/this.$get</Scope.prototype.$apply@http://localhost:3000/bower_components/angular/angular.js:17133:13
EventHandlerNonNull*createHttpBackend/<@http://localhost:3000/bower_components/angular/angular.js:11576:7
sendReq@http://localhost:3000/bower_components/angular/angular.js:11423:9
$http/serverRequest@http://localhost:3000/bower_components/angular/angular.js:11133:16
processQueue@http://localhost:3000/bower_components/angular/angular.js:15757:28
scheduleProcessQueue/<@http://localhost:3000/bower_components/angular/angular.js:15773:27
$RootScopeProvider/this.$get</Scope.prototype.$eval@http://localhost:3000/bower_components/angular/angular.js:17025:16
$RootScopeProvider/this.$get</Scope.prototype.$digest@http://localhost:3000/bower_components/angular/angular.js:16841:15
$RootScopeProvider/this.$get</Scope.prototype.$apply@http://localhost:3000/bower_components/angular/angular.js:17133:13
bootstrapApply@http://localhost:3000/bower_components/angular/angular.js:1713:9
invoke@http://localhost:3000/bower_components/angular/angular.js:4625:16
bootstrap/doBootstrap@http://localhost:3000/bower_components/angular/angular.js:1711:5
bootstrap@http://localhost:3000/bower_components/angular/angular.js:1731:12
angularInit@http://localhost:3000/bower_components/angular/angular.js:1616:5
@http://localhost:3000/bower_components/angular/angular.js:30709:5
jQuery.Callbacks/fire@http://localhost:3000/bower_components/jquery/dist/jquery.js:3187:11
jQuery.Callbacks/self.fireWith@http://localhost:3000/bower_components/jquery/dist/jquery.js:3317:7
.ready@http://localhost:3000/bower_components/jquery/dist/jquery.js:3536:3
completed@http://localhost:3000/bower_components/jquery/dist/jquery.js:3552:2
EventListener.handleEvent*jQuery.ready.promise@http://localhost:3000/bower_components/jquery/dist/jquery.js:3573:4
@http://localhost:3000/bower_components/jquery/dist/jquery.js:3583:1
@http://localhost:3000/bower_components/jquery/dist/jquery.js:34:3
@http://localhost:3000/bower_components/jquery/dist/jquery.js:15:2
 <div data-ng-animate="1" ui-view="" class="shuffle-animation ng-scope">

Solution

  • In the resolve part of your state you have to call the init method and not just access it:

    resolve: {
        'initData': function(currentUserDataService){
            // here seems to be the error
            return currentUserDataService.init();
        }
    }