1

I want to build an angular app that mostly serves as displaying data, but in a slightly dynamic way. I have multiple JSON files, with the same structure, but different content, one for each language:

res/information_en.json

[
    {
        "name": "Jobs",
        "data": [
            {
                "title": "Title",
                "employer": "Employer",
                "description": "Description",
                "begin": "2015-12",
                "end": "2016-12"
            }
        ]
    },{
        "name": "Personal",
        "data": [
            {
                "firstname": "Christian",
                "lastname": "Steinmeyer"
            }
        ]
    }
]

The German version:

res/information_de.json

[
    {
        "name": "Jobs",
        "data": [
            {
                "title": "Titel",
                "employer": "Arbeitgeber",
                "description": "Beschreibung",
                "begin": "2015-12",
                "end": "2016-12"
            }
        ]
    },{
        "name": "Personal",
        "data": [
            {
                "firstname": "Christian",
                "lastname": "Steinmeyer"
            }
        ]
    }
]

Additionally, I have another JSON file, that keeps track of all the languages:

res/languages.json

[
    {
        "name": "English",
        "short": "en",
        "active": true
    },{
        "name": "Deutsch",
        "short": "de",
        "active": false
    }
]

What I want, essentially, is for the user to be able to choose the language, the information should be displayed in, from the available ones, given by res/languages.json. For that, I have created a first service:

app/services/language-service.js

(function(){ 
  'use strict';

  angular.module('gulpAngularCv').factory('LanguageService', LanguageService);

  /** @ngInject */
  function LanguageService($log, $q, $resource, toastr) {

    var service = {};

    service.getLanguages = getLanguages;

    service.select = select;

    service.getActiveLanguage = getActiveLanguage;




    var initialized = false;

    var languages = [];

    function getLanguages(){
        if (initialized){
            return languages;
        } else {
            initialize().then(
                function success(result){
                    angular.forEach(result, function addLanguage(language){
                        languages.push(language);
                    })
                    initialized = true;
                }, function fail(reject){
                    $log.error("Loading 'res/languages.json' failed.");
                    $log.error(reject);
                    toastr.error('Make sure, it is formatted correctly.', 'Loading language file failed!');

                }
            );
            return languages;
        }
    }

    function initialize(){
        var deferred = $q.defer();
        $resource('res/languages.json').query(
            function success(result){
                deferred.resolve(result);
            }, function fail(reject){
                deferred.reject(reject);
            }
        );
        return deferred.promise;
    }

    function select(language){
        // iterate over all languages
        // deactivate, if active and activate if equal to parameter
    }

    function getActiveLanguage(){
        for (var i = 0; i < languages.length; i++){
            if (languages[i].active){
                return languages[i];
            }
        }
    }

    return service;

  }
})();

This, for itself works like a charm, when called from a controller. But as I said, I wanted to be able to load the information from the according json file, as well. Which I try with the next service:

app/services/information-service.js

(function(){ 
  'use strict';

  angular.module('gulpAngularCv').factory('InformationService', InformationService);

  /** @ngInject */
  function InformationService($log, $q, $resource, toastr, LanguageService) {

    var service = {};

    service.getInformation = getInformation;




    var initialized = {};

    var information = [];

    function getInformation(){
        var language = LanguageService.getActiveLanguage();
        if (initialized === language){
            return information;
        } else {
            initialize(language).then(
                function success(result){
                    angular.forEach(result, function addInformation(information){
                        information.push(information);
                    })
                    initialized = language;
                }, function fail(reject){
                    $log.error("Loading 'res/information_" + language.short + ".json' failed.");
                    $log.error(reject);
                    toastr.error('Make sure, it is formatted correctly.', 'Loading information file failed!');

                }
            );
            return information;
        }
    }

    function initialize(language){
        var deferred = $q.defer();
        $resource("res/information_" + language.short + ".json").query(
            function success(result){
                deferred.resolve(result);
            }, function fail(reject){
                deferred.reject(reject);
            }
        );
        return deferred.promise;
    }

    return service;

  }
})();

I, basically, do the same thing but this time, it won't work, because it seems, this service is injected first, even though it depends on the other one. I get the following error in my browser's console:

TypeError: Cannot read property 'short' of undefined
    at initialize (http://localhost:3000/app/services/information-service.js:44:48)
    at Object.getInformation (http://localhost:3000/app/services/information-service.js:25:13)
    at new MainController (http://localhost:3000/app/main/main-controller.js:12:40)
    at invoke (http://localhost:3000/bower_components/angular/angular.js:4535:17)
    at Object.instantiate (http://localhost:3000/bower_components/angular/angular.js:4543:27)
    at http://localhost:3000/bower_components/angular/angular.js:9395:28
    at link (http://localhost:3000/bower_components/angular-route/angular-route.js:977:26)
    at invokeLinkFn (http://localhost:3000/bower_components/angular/angular.js:9039:9)
    at nodeLinkFn (http://localhost:3000/bower_components/angular/angular.js:8533:11)
    at compositeLinkFn (http://localhost:3000/bower_components/angular/angular.js:7929:13) <div ng-view="" class="ng-scope">

As I see it, this error is weird, because the promised should already be resolved, by the time of the call, the way I implemented it.

For the sake of integrity, here the MainController as well:

app/main/main-controller.js

(function() {
  'use strict';

  angular
    .module('gulpAngularCv')
    .controller('MainController', MainController);

  /** @ngInject */
  function MainController(InformationService) {
    var vm = this;

    vm.categories = InformationService.getInformation();
  }
})();

I've looked at this and this question already, as well as the official documentation, but they only got me so far...

1 Answer 1

0

After all, I believe the cause of my problem was visibility and scopes in Javascript. In the information service (posted in the question), I used a "global" variable with the name information, but within the angular for each loop in the getInformation() method, I create a local variable with the same name, so that nothing ever was added to my original variable, as I intended to. For completion, I'll add my final implementation of the two services again. Note, that I not only solved the bug, but also did some refactoring in the information service (below). This solution works, as I intend it to.

app/services/language-service.js

(function(){ 
  'use strict';

  angular.module('gulpAngularCv').factory('LanguageService', LanguageService);

  /** @ngInject */
  function LanguageService($log, $q, $resource, toastr) {

    var service = {};

    service.getLanguages = getLanguages;

    service.select = select;

    service.getActiveLanguage = getActiveLanguage;




    var initialized = false;

    var languages = [];

    function getLanguages(){
        if (initialized){
            return languages;
        } else {
            initialize().then(
                function success(result){
                    $log.debug("Loaded languages from 'res/languages.json'");
                    $log.debug(result);
                    angular.forEach(result, function addLanguage(language){
                        $log.debug(language);
                        languages.push(language);
                    })
                    initialized = true;
                }, function fail(reject){
                    $log.error("Loading 'res/languages.json' failed.");
                    $log.error(reject);
                    toastr.error('Make sure, it is formatted correctly.', 'Loading language file failed!');

                }
            );
            return languages;
        }
    }

    function initialize(){
        var deferred = $q.defer();
        $resource('res/languages.json').query(
            function success(result){
                deferred.resolve(result);
            }, function fail(reject){
                deferred.reject(reject);
            }
        );
        return deferred.promise;
    }

    function select(language){
        // iterate over all languages
        // deactivate, if active and activate if equal to parameter
    }

    function getActiveLanguage(){
        for (var i = 0; i < languages.length; i++){
            if (languages[i].active){
                return languages[i];
            }
        }
    }

    return service;

  }
})();

and the information service:

app/services/information-service.js

(function(){ 
  'use strict';

  angular.module('gulpAngularCv').factory('InformationService', InformationService);

  /** @ngInject */
  function InformationService($log, $q, $resource, $rootScope, toastr, LanguageService) {

    var service = {};

    service.getCategories = getCategories;


    var model = {};
    model.initialized = null;
    model.categories = [];

    function getCategories(){
        $log.debug("getCategories");
        loadData();
        return model.categories;
    }

    function loadData(){
        var language = LanguageService.getActiveLanguage();
        if (language && model.initialized !== language){
            initialize(language).then(
                function success(data){
                    model.categories.length = 0;
                    angular.forEach(data.categories, function addInformation(datum){
                        $log.debug(datum);
                        model.categories.push(datum);
                    })
                }, function fail(reject){
                    toastr.error('Make sure, it is formatted correctly.', 'Loading information file failed!');
                }
            );
        }
    }

    function initialize(language){
        var deferred = $q.defer();
        $resource("res/information_" + language.short + ".json").get(
            function success(result){
                deferred.resolve(result);
                model.initialized = language;
            }, function fail(reject){
                deferred.reject(reject);
            }
        );
        return deferred.promise;
    }

    return service;

  }
})();
Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.