Angular: load environment properties before config/run - angularjs

I'm developing a angular app, and this app has about a 10 configurable properties (depending on the environment and client).
I had those properties in json config files, but this is really troublesome: there must be specific builds per env/company. So I would like to retrieve those properties once from the backend on app load.
So in order to do this I created a Provider
var app = angular.module('myApp', [...]);
app.provider('environment', function() {
var self = this;
self.environment;
self.loadEnvironment = function (configuration, $http, $q) {
var def = $q.defer();
$http(...)
.success(function (data) {
self.environment = Environment.build(...);
def.resolve(self.environment);
}).error(function (err) {
...
});
return def.promise;
};
self.$get = function(configuration, $http, $q) {
if (!_.isUndefined(self.environment)) {
return $q.resolve(self.environment);
}
return self.loadEnvironment(configuration, $http, $q);
};
}
app.config(... 'environmentProvider', function(... environment) {
...
//The problem here is that I can't do environment.then(...) or something similar...
//Environment does exists though, with the available functions...
}
How to properly work with this Provider that executes a rest call to populate his environment variable?
Thanks in advance!

This is an excelent scenario to explore angularjs features.
Assuming that you really need the environment data loaded before the app loads, you can use angular tools to load the environment and then declare a value or a constant to store your environment configs before the app bootstraps.
So, instead of using ng-app to start your app, you must use angular.bootstrap to bootstrap it manually.
Observations: You mustn't use ng-app once you are bootstrapping the app manually, otherwise your app will load with the angular default system without respecting your environment loading. Also, make sure to bootstrap your application after declare all module components; i.e. declare all controllers, servieces, directives, etc. so then, you call angular.bootstrap
The below code implements the solution described previously:
(function() {
var App = angular.module("myApp", []);
// it will return a promisse of the $http request
function loadEnvironment () {
// loading angular injector to retrieve the $http service
var ngInjector = angular.injector(["ng"]);
var $http = ngInjector.get("$http");
return $http.get("/environment-x.json").then(function(response) {
// it could be a value as well
App.constant("environment ", response.data);
}, function(err) {
console.error("Error loading the application environment.", err)
});
}
// manually bootstrap the angularjs app in the root document
function bootstrapApplication() {
angular.element(document).ready(function() {
angular.bootstrap(document, ["myApp"]);
});
}
// load the environment and declare it to the app
// so then bootstraps the app starting the application
loadEnvironment().then(bootstrapApplication);
}());

Related

Browserify AngularJS Module factory method extend another factory method

I'm currently attempting to implement Browserify over an existing application.
I have a requirement where I have a BaseService that contains a bunch of standard functionality e.g. setting standard headers on requests etc.
In my factories I use loadash to extend the BaseService. For this to work I need to have a reference to BaseService in any factory that tries to extend it. I can't figure out how to pass through this dependency now I have started to use browserify.
I've added sample code below.
Module declaration:
'use strict';
var angular = require('angular');
module.exports = angular.module('todoApp.services', [require('../secure').name])
.factory('AuthService', ['$q', 'ConsumerConfig', require('./auth-service')])
.factory('BaseWebService', ['$http', '$q', 'Encryption', 'nativeCrypto', require('./base-web-service')]);
AuthService:
'use strict';
var _ = require('lodash');
module.exports = function($q, ConsumerConfig) {
return _.extend({
config: ConsumerConfig,
authenticate: function (options) {
var deferred = $q.defer();
this.callService({
user: options.user,
url: "/AuthenticateUser",
type: "GET"
}).then(function (response) {
deferred.resolve(response.data.userAuthResponse.responseMessage.Token);
}, function (error) {
deferred.reject(error);
});
return deferred.promise;
}
}, BaseWebService);
};
I would ideally like to use DI to inject the dependency but when I try this I keep getting an unknown provider error. Does anyone know how I can get this working?
For anyone looking - I solved this by creating a new "Core" module and then having my services module require the core module.
This way everything was loaded in the correct order

Angular Services within Modules

Having difficulty injecting a service into another service. I want a service hierarchy, where I can pass/request logic to/from the parent service, for sake of brevity and encapsulation.
So for example, I have a userService, and I want the userService to manage the user's toDoList. So I created a toDoService, and I want controllers to add a toDo for the user by passing the request to the userService, which relays to the toDoService. Here's what I have:
// app.js
angular.module('myApp', [
// other dependencies...
'myApp.myServices'
]);
// services/toDoService.js
angular.module('myApp.myServices', [])
.factory('toDoService', function($http) {
getStuff = function(userId) {
// returns $http call
};
addStuff = function(userId, data) {
// returns $http call
};
});
// services/userService.js
angular.module('myApp.myServices', [])
.factory('userService',
['$http', 'toDoService', function(
$http, toDoService) {
addToDo = function(data) {
toDoService.addStuff(user.uid, data)
.then(function(success) {
// apply bindings
})
.catch(function(error) {
// display error
});
};
getToDos = function(data) {
toDoService.getStuff(user.uid)
.then(function(success) {
// apply bindings
})
.catch(function(error) {
// display error
});
};
}]);
The controller works with the userService, and code from toDoService worked when it was originally in userService. But when I create the toDoService and move that code there and encapsulate it, angular complains about toDoService.
Error: [$injector:unpr] Unknown provider: toDoServiceProvider <- toDoService <- userService
I've checked script references, and all scripts are properly included. e.g. <script src='/[..]/toDoService.js' /> etc...
So I'm wondering is it even possible to inject a service into another service within the same module? Is it an issue with my naming convention?
angular.module('myApp.myServices', [])
This line in userService.js defines the module myApp.services, overwriting the one that has previously been defined in toDoService.js.
Define the module only once (in a separate file). Get a reference to this previously defined module using
angular.module('myApp.myServices')
i.e. without the empty array as second argument.

AngularJS - Running a function once on load

I am trying to run an $http function when my AngularJS application first loads.
This $http function needs to finish before any of the controllers in my application could properly function. How would I go about doing this? This sounds like a promise, but it sounds like I would be creating a promise in each controller...
I currently have the function that I want to run first like this:
app.run(function() {
$http.get('link').success(function(data) {
// success function. The data that I get from this HTTP call will be saved to a service.
}).error(function(error) {
});
});
However, sometimes the controller will load before the http call finishes.
The problem
Angular is not dynamic, you cannot add controller dynamically neither factory, etc. Also you cannot defer controller bootstrap, angular loads everything together, and it's quite disadvantage (will be fixed in Angular 2)
The cure
But javascript itself has very important feature - closure, which works anywhere, anytime.
And angular has some internal services that can be injected outside of angular ecosystem, even into browser console. Those services injected as shown below. We technically could use anything else (jQuery.ajax, window.fetch, or even with XMLHttpRequest), but let's stick with total angular solution
var $http_injected = angular.injector(["ng"]).get("$http");
The act
First of all, we defer whole angular app bootstrap, inject http service. Then you make your needed request, receive data and then closure get's to work, we pass received data into some service, or we could also assign in to some angular.constant or angular.value but let's just make demo with angular.service, so when your service has data, bootstrap whole app, so that all controllers get initialized with your needed data
Basically that kind of tasks solved like this
<body>
<div ng-controller="Controller1">
<b>Controller1</b>
{{text}}
{{setting.data.name}}
</div>
<hr>
<div ng-controller="Controller2">
<b>Controller2</b>
{{text}}
{{setting.data.name}}
</div>
<script>
//define preloader
var $http_injected = angular.injector(["ng"]).get("$http");
$http_injected.get('http://jsonplaceholder.typicode.com/users/1').then(function(successResponse) {
//define app
angular.module('app', []);
//define test controllers
//note, usually we see 'controller1 loaded' text before 'settings applied', because controller initialized with this data, but in this demo, we will not see 'controller1 loaded' text, as we use closure to assign data, so it's instantly changed
angular.module('app').controller('Controller1', function($scope, AppSetting) {
$scope.text = 'controller1 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
angular.module('app').controller('Controller2', function($scope, AppSetting) {
$scope.text = 'controller2 loaded';
$scope.setting = AppSetting.setting;
$scope.$watch('setting', function(e1 ,e2){
$scope.text = 'settings applied'
});
});
//define test services, note we assign it here, it's possible
//because of javascript awesomeness (closure)
angular.module('app').service('AppSetting', function() {
this.setting = successResponse;
});
//bootstrap app, we cannot use ng-app, as it loads app instantly
//but we bootstrap it manually when you settings come
angular.bootstrap(document.body, ['app']);
});
</script>
</body>
Plunker demo
You can do this when you configure your routes
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider
.when('/', {
controller: 'MainCtrl',
templateUrl: 'main.html',
resolve: {
data: ['$http',
function($http)
{
return $http.get('/api/data').then(
function success(response) { return response.data.rows[0]; },
function error(reason) { return false; }
);
}
]
}
});
}]);
Similar question:
AngularJS - routeProvider resolve calling a service method
AngularJS: $routeProvider when resolve $http returns response obj instead of my obj
Heres a plunkr I found using a service, which is what I would recommend.
http://plnkr.co/edit/XKGC1h?p=info

Advantage defining a Angular Module / Service under a different name then the main application

Where is the advantage in defining an angular module as such:
var app = angular.module('app', ['appServices']);
var appServices = angular.module('appServices', []);
appServices.factory('someService', function ($http) {
return { member:true }
});
... as opposed to:
var app = angular.module('app', []);
app.factory('someService', function ($http) {
return { member:true }
});
I cannot see a direct advantage, given that my single page app is named 'app'. Keep in mind I am new to Angular.
You can manage dependencies in modules. You code will be better organized and everyone can tell you what modules depend on other modules.
Thanks

Wire up AngularJS controller to express controller

I'm getting started with node/express/Angular by using the MEAN stack at mean.io.
I don't understand how the Angular controller calls the express controller to fetch data.
What I have is public/js/controllers/index.js:
angular.module('mean.system').controller('IndexController', ['$scope', 'Global', 'Tabs',
function ($scope, Global, Tabs) {
$scope.global = Global;
Tabs.query(function(tabs) {
$scope.tabs = tabs;
});
}]);
But I'm confused what exactly 'Tabs' is. I know that somehow, magically, eventually this method is called - I think this is the Express controller?
app/controllers/tabs.js:
exports.all = function(req, res) {
Tab.find().sort('artist').select("-content").populate('user').exec(function(err, tabs) {
if (err) {
res.render('error', {
status: 500
});
} else {
res.jsonp(tabs);
}
});
};
But I don't understand how it gets called. What I want to do is call a different method in app/controllers/tabs.js instead - namely, this:
exports.newest = function(req, res) {
Tab.find().sort('-created').limit(10).select("-content").exec(function(err, tabs) {
...
But I don't understand how to "wire up" the AngularJS controller with the express controller.
i.e. what do I have to do so that I can do something like this in my controller:
angular.module('mean.system').controller('IndexController', ['$scope', 'Global', 'Tabs',
function ($scope, Global, Tabs) {
$scope.global = Global;
Tabs.newest(function(tabs) { // this won't work
$scope.tabs = tabs;
});
}]);
In MEAN, The Articles service is an angular service that returns a $resource object you can usually find in the public/js/services folder.
$resource is a wrapper around $http AJAX service that comes with angularjs, it enables you to connect with RESTful endpoints if your REST service is built in a specific manner.
The connection to the the node.js controller happens using the routes.js object found in the config folder that binds routes to specific module methods.
further reading:
http://docs.angularjs.org/api/ngResource.$resource
http://expressjs.com/api.html#app.VERB

Resources