Is storing variables in $rootScope good practice?
Asked Answered
J

2

6

Update 2: I found a solution

I've altered the settings file to just a JS file, added var tempSettings = to the beginning of the file, and added it in index.html. This way it gets loaded with the initial HTML, ensuring it will exist when app.run goes. The settings service then takes this tempSettings variable and puts it in the service. To clean up, I remove the tempSettings pointer.

New settings file called settings.js

var tempSettings = {
  "environment": "development",
[...]

Added to index.html:

<script src="settings.js"></script>

Service:

myApp.service("settings", function(){
  var settings = null;

  this.initialize = function() {
    settings = tempSettings;
    tempSettings = undefined;
  };

  this.get = function() {
    return settings;
  }

});

Update 1: I found a problem

Because the settings file is loaded async, it sometimes happens that a module tries to use the settings before they are loaded. I'll keep you updated on solutions. I have moved the settings into a service, that is definitely better.

Original question

When I google how to store environment settings in AngularJS apps, I come across options using Grunt or Gulp (and there's probably others as well), but to me this option seems much more obvious. Meaning that there's probably a good reason not to use it. Is this way of storing settings a bad idea?

I have in my app root a file called settings.json which looks something like this:

{
  "settingsFile": true,
  "environment": "development",
  "logLevel": "debug",
  "userApiBase": "http://localhost/covlelogin/web/api/",
  "oAuth": {
    "google":{
      "endpoint": "https://accounts.google.com/o/oauth2/auth",
      "clientId": "12345",
      "scope": "email profile",
      "state": "MyToken123",
      "redirectUri": "http://localhost/loginadmin/web/oAuthRedirect",
      "responseType": "code",
      "approvalPrompt": "force"
    }
  }
}

I then have a little bit in app.run that looks like this:

MyApp.run(function ($rootScope, $http) {
  //Load settings
  $http.get('settings.json').
      success(function (settings) {
        if (settings.settingsFile){
          $rootScope.settings = settings;
          console.log("Settings loaded");
        }else{
          console.log("Error loading settings. File may be corrupt.");
          //Additional error handling
        }
      }).
      error(function (data) {
        console.log("Error getting settings file.");
        //Additional error handling
      })
});

And now whenever I need a setting I can always go to $rootScope.settings.userApiBase or whatever. It makes sense to me, because all I have to do is to make sure settings.json is ignored on check-in. The whole method is really straight forward. Is there a flaw to this design?

Jackhammer answered 28/5, 2015 at 16:25 Comment(0)
T
8

Try not to pollute the $rootScope Here as much as possible. Make a Settings service that handles the settings instead. Services are singleton objects so once you initialize the service, the settings will be available everywhere you inject the service.

MyApp.service("Settings", function($http) {

    var settings = null;

    this.initialize = function() {
        $http.get('settings.json').success(function (s) {
            if (s.settingsFile){
                settings = s;
                console.log("Settings loaded");
            } else {
                console.log("Error loading settings. File may be corrupt.");
                //Additional error handling
            }
        }).error(function (data) {
            console.log("Error getting settings file.");
            //Additional error handling
        })
    };

    this.get = function() {
        return settings;
    }

    return this;
});

And in MyApp.run:

MyApp.run(function (Settings) {
    Settings.initialize();
}

Then, whenever you want to access Settings in a controller or another service or something, simply call Settings.get() which will return your settings. Just make sure to inject the Settings service into anything that uses it (like I did in the second code block).

Threaten answered 28/5, 2015 at 20:54 Comment(1)
I don't find the reason for "not to pollute the $rootScope". Can you provide any link to read?Pediment
C
3

In general, you should avoid polluting the rootScope when possible. I like that you are loading the settings in app.run(). What about introducing a SettingsService that is populated in app.run, and can be injected into your other controllers/services? This has the added value of being mockable during unit tests.

Here is a plunker

app.run(function(SettingsService) {
  SettingsService.name = "Alex";
  SettingsService.password = "pw1";
})

app.controller('MainCtrl', function($scope, SettingsService) {
  $scope.settings = SettingsService;
});

app.factory('SettingsService', function() {
  return {}
})
Convalescent answered 28/5, 2015 at 16:30 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.