53

I need to load a config file (JSON format) upon my AngularJS app startup in order to load few parameters which will be used in all api calls. So I was wondering if it is possible to do so in AngularJS and if yes where / when I shall be loading the config file?

Note: - I will need to save the config file parameters in a service, so I will need to load the json file content before any controller is loaded but with service units available - Using an external json file is a must in my case here as the app client need to be able to update the app configuration easily from external file without the need to go through the app sources.

MChan
  • 6,842
  • 27
  • 83
  • 132
  • Is this configuration data that will load from a web service, or will it be a static file that you have control over? If it is static, you could just create an angular service that has the static data within it in lieu of a .json file. – Keith Morris Apr 03 '14 at 00:46
  • Yea please give us an example of the type of data you're looking to load and where it comes from/is stored. – tommybananas Apr 03 '14 at 00:47
  • @KeithMorris it will be a static file that I have control over. But I am wondering where exactly inside the app I should load it to ensure that it is loaded before any controller is loaded and only once in applications start? – MChan Apr 03 '14 at 00:48
  • @snowman4415 Example of data is some formats, constants, web service api url...etc – MChan Apr 03 '14 at 00:50

5 Answers5

27

EDITED

It sounds like what you are trying to do is configure a service with parameters. In order to load the external config file asynchronously, you will have to bootstrap the angular application yourself inside of a data load complete callback instead of using the automatic boostrapping.

Consider this example for a service definition that does not actually have the service URL defined (this would be something like contact-service.js):

angular.module('myApp').provider('contactsService', function () {

    var options = {
        svcUrl: null,
        apiKey: null,
    };

    this.config = function (opt) {
        angular.extend(options, opt);
    };

    this.$get = ['$http', function ($http) {

        if(!options.svcUrl || !options.apiKey) {
            throw new Error('Service URL and API Key must be configured.');
        }

        function onContactsLoadComplete(data) {
            svc.contacts = data.contacts;
            svc.isAdmin = data.isAdmin || false;
        }

        var svc =  {
            isAdmin: false,
            contacts: null,
            loadData: function () {
                return $http.get(options.svcUrl).success(onContactsLoadComplete);
            }
        };

        return svc;
    }];
});

Then, on document ready, you would make a call to load your config file (in this case, using jQuery). In the callback, you would then do your angular app .config using the loaded json data. After running the .config, you would then manually bootstrap the application. Very Important: do not use the ng-app directive if you are using this method or angular will bootstrap itself See this url for more details:

http://docs.angularjs.org/guide/bootstrap

Like so:

angular.element(document).ready(function () {
    $.get('/js/config/myconfig.json', function (data) {

        angular.module('myApp').config(['contactsServiceProvider', function (contactsServiceProvider) {
            contactsServiceProvider.config({
                svcUrl: data.svcUrl,
                apiKey: data.apiKey
            });
        }]);

        angular.bootstrap(document, ['myApp']);
    });
});

UPDATE: Here is a JSFiddle example: http://jsfiddle.net/e8tEX/

Keith Morris
  • 2,125
  • 1
  • 19
  • 21
  • unfortunately using an external configuration file is a must, as it is a client requirement so that he can easily update the configuration of the app without the need to go through the app source code – MChan Apr 03 '14 at 01:32
  • @MChan I just updated the code to show how to do this with manual bootstrapping and with a link to some additional information. – Keith Morris Apr 03 '14 at 19:54
  • Thanks a lot for the detailed explanation. I already have a service which is responsible for loading the JSON, so I am wondering can I call it from angular.element(document).ready ?If you do can you please show me an example – MChan Apr 03 '14 at 23:48
  • I'm not sure this could be done directly with Angular. It seems like the idea of what you are wanting to do is bootstrap your application with some initial configuration data on which the Angular app depends. So it would seem that the data should be loaded first and then your app configured using that data. If you already have a 'configService', instead of using that service to load the data, in the .config() call use the configSeviceProvider to initialize the configService with the loaded data (as in my example). It seems cleaner and less complex. – Keith Morris Apr 04 '14 at 15:30
  • That's exactly what I am trying to do, I must load some config data from an external json file and have this info loaded 'before' the Angular app is loaded. I've tried your example as is but I can't get the configService initialized or the $http service to load the json file in ConfigService...not sure what I could be missing but I always get injector error – MChan Apr 04 '14 at 17:49
  • You have to do it outside of angular (i.e. don't use the $http service). Use jQuery or native javascript to load the file then use the data with the .config() method. – Keith Morris Apr 04 '14 at 18:00
  • Thanks Keith but I am sorry I am not clear 100% with the idea, so any chance you can please give me a small example on how to do that? Thanks – MChan Apr 04 '14 at 21:30
  • @MChan I just added a JS Fiddle to explain further: http://jsfiddle.net/e8tEX/ There is some extra stuff in there that is used to configure the /echo/json/ service that JSFiddle supplies, but it is just to demonstrate loading some data from a file (Like your json file) and then configuring the service with it. – Keith Morris Apr 06 '14 at 16:29
  • Interesting, I have this working using $http injected within my ready method however I found that this only works if I manually include jquery script tags in page before the AngularJS script tags. Using the minimal JQuery included with AngularJS does not appear result in $http service from being injected into the ready method... strange. – jpierson Nov 18 '14 at 05:06
  • Thanks a lot for this. It seems this still holds true after 2 years. It is a shame angular doesn't support this requirement out of the box. It took me a while to get my head around _providers_, _services_ and _factories_ but I was able to do it. Thanks again @KeithMorris! – Mário Areias Jul 05 '16 at 02:25
22

I couldn't get the approach suggested my Keith Morris to work.

So I created a config.js file and included it in index.html before all the angular files

config.js

var configData = {
    url:"http://api.mydomain-staging.com",
    foo:"bar"
}

index.html

...
<script type="text/javascript" src="config.js"></script>
<!-- compiled JavaScript --><% scripts.forEach( function ( file ) { %>
<script type="text/javascript" src="<%= file %>"></script><% }); %>

then in my run function I set the config variables to $rootScope

.run( function run($rootScope) {
    $rootScope.url = configData.url;
    $rootScope.foo = configData.foo;
    ...
})
mike
  • 1,583
  • 1
  • 19
  • 35
1

You can use constants for things like this:

angular.module('myApp', [])

// constants work
//.constant('API_BASE', 'http://localhost:3000/')
.constant('API_BASE', 'http://myapp.production.com/')
//or you can use services
.service('urls',function(productName){ this.apiUrl = API_BASE;})

//Controller calling
.controller('MainController',function($scope,urls, API_BASE) {
     $scope.api_base = urls.apiUrl; // or API_BASE
});

//in html page call it {{api_base}}

There are also several other options including .value and .config but they all have their limitations. .config is great if you need to reach the provider of a service to do some initial configuration. .value is like constant except you can use different types of values.

https://stackoverflow.com/a/13015756/580487

Community
  • 1
  • 1
tommybananas
  • 5,718
  • 1
  • 28
  • 48
  • how do you access `constant` in a controller? – chovy Jul 19 '15 at 04:39
  • 1
    @chovy, set constant name as dependency for module, that uses it. – blazkovicz Aug 11 '15 at 05:55
  • 5
    No this doesn't work. The url can't be hard-coded. It needs to read it from a file, using $http to get the value. If you do this, then your minimized js after build contains the url. Then, your whole script is environment specific and has to be redeployed in every environment. – toddmo Oct 14 '15 at 15:41
0

Solved by using constant. Like providers you can configure it in .config phase. Everything else like Keith Morris wrote before. So the actual code would be look like this:

(function () {
var appConfig = {

};

angular.module('myApp').constant('appConfig', appConfig);
})();

then in app.bootstrap.js

(function () {

angular.element(document).ready(function () {

    function handleBootstrapError(errMsg, e) {
        console.error("bootstrapping error: " + errMsg, e);
    }

    $.getJSON('./config.json', function (dataApp) {
        angular.module('myApp').config(function (appConfig) {
            $.extend(true, appConfig, dataApp);
            console.log(appConfig);
        });

        angular.bootstrap(document, ['myApp']);

    }).fail(function (e) {
        handleBootstrapError("fail to load config.json", e);
    });

});
})();
0

To json config file, there is a practice example on Jaco Pretorius blog's. Basically:

angular.module('plunker', []);
angular.module('plunker').provider('configuration', function() {
  let configurationData;

  this.initialize = (data) => {
    configurationData = data;
  };

  this.$get = () => {
    return configurationData;
  };
});

angular.module('plunker').controller('MainCtrl', ($scope, configuration) => {
  $scope.externalServiceEnabled = configuration.external_service_enabled;
  $scope.externalServiceApiKey = configuration.external_service_api_key;
});

angular.element(document).ready(() => {
  $.get('server_configuration.json', (response) => {
    angular.module('plunker').config((configurationProvider) => {
      configurationProvider.initialize(response);
    });

    angular.bootstrap(document, ['plunker']);
  });
});

Plunker: http://plnkr.co/edit/9QB6BqPkxprznIS1OMdd?p=preview

Ref: https://jacopretorius.net/2016/09/loading-configuration-data-on-startup-with-angular.html, last access on 13/03/2018

Gilberto Alexandre
  • 2,227
  • 1
  • 18
  • 20