Search code examples
javascriptangularjsangular-ui-routerangularjs-service

AngularJS ui-router: how to resolve typical data globally for all routes?


I have an AngularJS service which communicates with the server and returns translations of different sections of the application:

angular
     .module('utils')
     .service('Translations', ['$q','$http',function($q, $http) {
        translationsService = {
            get: function(section) {
                if (!promise) {
                    var q = $q.defer();
                    promise = $http
                            .get(
                                '/api/translations',
                                {
                                    section: section
                                })
                            .success(function(data,status,headers,config) {
                                q.resolve(result.data);
                            })
                            .error(function(data,status,headers,config){ 
                                q.reject(status);
                            });
                    return q.promise;
                }
            }
        };

        return translationsService;
    }]);

The name of the section is passed as the section parameter of the get function.

I'm using AngularJS ui-router module and following design pattern described here

So I have the following states config:

angular.module('app')
    .config(['$stateProvider', function($stateProvider) {
    $stateProvider
    .state('users', {
        url: '/users',
        resolve: {
            translations: ['Translations',
                function(Translations) {
                    return Translations.get('users');
                }
            ]            
        },
        templateUrl: '/app/users/list.html',
        controller: 'usersController',
        controllerAs: 'vm'
    })
    .state('shifts', {
        url: '/shifts',
        resolve: {
            translations: ['Translations',
                function(Translations) {
                    return Translations.get('shifts');
                }
            ]            
        },
        templateUrl: '/app/shifts/list.html',
        controller: 'shiftsController',
        controllerAs: 'vm'
    })

This works fine but as you may notice I have to explicitly specify translations in the resolve parameter. I think that's not good enough as this duplicates the logic.

Is there any way to resolve translations globally and avoid the code duplicates. I mean some kind of middleware.

I was thinking about listening for the $stateChangeStart, then get translations specific to the new state and bind them to controllers, but I have not found the way to do it.

Any advice will be appreciated greatly.

Important note: In my case the resolved translations object must contain the translations data, not service/factory/whatever.

Kind regards.


Solution

  • Though this is a very old question, I'd like to post solution which I'm using now. Hope it will help somebody in the future. After using some different approaches I came up with a beautiful angularjs pattern by John Papa

    He suggest using a special service routerHelperProvider and configure states as a regular JS object. I'm not going to copy-paste the entire provider here. See the link above for details. But I'm going to show how I solved my problem by the means of that service.

    Here is the part of code of that provider which takes the JS object and transforms it to the states configuration:

    function configureStates(states, otherwisePath) {
        states.forEach(function(state) {
            $stateProvider.state(state.state, state.config);
    });
    

    I transformed it as follows:

    function configureStates(states, otherwisePath) {
    
        states.forEach(function(state) {
    
            var resolveAlways = {
    
                translations: ['Translations', function(Translations) {
    
                    if (state.translationCategory) {
    
                        return Translations.get(state.translationCategory);
    
                    } else {
    
                        return {};
    
                    }
    
                }],
    
            };  
    
    
    
            state.config.resolve =
    
                angular.extend(state.config.resolve || {}, resolveAlways || {});
    
    
    
            $stateProvider.state(state.state, state.config);
    
        }); 
    
    }); 
    

    And my route configuration object now looks as follows:

            {
                state: ‘users’,
                translationsCategory: ‘users’,
                config: {
                    controller: ‘usersController’
                    controllerAs: ‘vm’,
                    url: ‘/users’.
                    templateUrl: ‘users.html'
                }
    

    So what I did:

    I implemented the resolveAlways object which takes the custom translationsCategory property, injects the Translations service and resolves the necessary data. Now no need to do it everytime.