How to retrieve constants for AngularJS from backend - angularjs

i have some application settings that i want to retrieve from backend, so that they would be available to all of my application controllers via injection. What is the most angular-way to do that?
1) If i only needed settings for one or few controllers i could retrieve them via routing resolve method, but what about global application scope?
2) I could use the .run() method, but since call will be async i have no guaranties that my config will be loaded before i access controllers.
Currently my settings are returned as a json object, and my templates/html files are simply served by web server. So i cannot embed anything into script tags, parse html on the server side or any similar technique.

I would create a service for your settings. Then using the .run() method, called a service that returns your app settings data:
angular
.module('YourApp',[])
.service('Settings',function(){
this.data = {}
})
.run(function(Settings,$http){
$http
.get('/ajax/app/settings')
.success(function(data){
Settings.data = data
})
})
function Controller($scope,Settings){
// use your Settings.data
}
http://docs.angularjs.org/api/angular.Module#methods_run

There is a neat plugin to do the job. https://github.com/philippd/angular-deferred-bootstrap
You need to change bootstrap method
deferredBootstrapper.bootstrap({
element: document.body,
module: 'MyApp',
resolve: {
APP_CONFIG: function ($http) {
return $http.get('/api/demo-config');
}
}
});
angular.module('MyApp', [])
.config(function (APP_CONFIG) {
console.log(APP_CONFIG);
});

Related

Waiting until $resource finished to load in my controller before continuing

I'm building a rest api with fosrestbundle and I manage the frontend with angular and Twig template. One on my url address looks like this :
http://mywebsite/myroute/idContain
When I load the url in my browser, in a twig template (kind of html), I retrieve the parameter "idContain" (comming from a fosrestbundle controller) with ng-init of angularjs like this :
<div class="container-fluid" ng-init="getContainByID({{ idContain }})">
//...lot html div with angularjs directives
</div>
And immediately, ng-init will go to my angularJS app finds getContainByID(idContain) to run it.
This one looks like this :
angular.module("myApp", ["ngSanitize", 'angular.filter', 'ui.tinymce', ...])
.config(function($interpolateProvider, ...) {
$interpolateProvider.startSymbol('{[{').endSymbol('}]}');
})
.controller("myCtrl",function ($filter,..., myService)
{
// lot of code...
$scope.getContainByID = function(idContain)
{
$scope.currentContain = myService.getContains(idContain);
$scope.containRoot = $scope.currentContain.contain.containRoot;
...
}
// lot of code...
}
The fact is that, myService.getContains(idContain) come from my rest service looking like this :
angular.module("MyServiceRest", ['ngResource'])
.factory("myService", function ($rootScope, $resource) {
var apiData = $resource(
"/api", {},
{
...
"getContains": {method: 'GET', isArray: false, url: "mywebsite/api/myroute/:containid"}
...
});
return {
getContains: function (idContain) {
return apiData.getContains({containid: idContain});
}
}
});
Now the problem is, when I run my angularjs App, $scope.containRoot doesn't wait until myService.getContains(idContain) (coming from my asynchroeous $resource service) finished to load, and caused errors making my webapp crash.
How can I do, to force $scope.containRoot and the rest of my angular code to waiting until myService.getContains(idContain) (connected to the api resource) completly finished to load before continuing ?
$resource makes an asynchronous request but immediately reurns an empty object or array.
There are numerous ways you could handle your issue.
One would be not to worry about declaring $scope.containRoot and just using currentContain.contain.containRoot in the view. This property will get rendered after the request is received
Another is to use the $promise that is also returned by $resource and assign the scope property in promise callback
$scope.currentContain = myService.getContains(idContain);
$scope.currentContain.$promise.then(function(){
$scope.containRoot = $scope.currentContain.contain.containRoot;
});
Another is to use a routing resolve based on the same promise so the route( or state depending on router) is noot entered until the request is complete

Configuring UI Router states based on configuration

Is there a way to configure a UI Router states based on Server Side JSON file. I wan't to avoid hard coding the States inside my module.config(..).
I firstly thought of having controller which has the state map data available which can just call $stateProvider. However, I believe, controllers cannot have providers injected into them.
The other option I have thought was having a Javascript file outside of angular which puts the state configuration data in some global variable to be referenced from Module config function.
But is there a better approach?
I would say, that there are in general two ways how to use SERVER data (JSON) to build up states.
Firstly, we can use $http to load the JSON:
AngularJS - UI-router - How to configure dynamic views
The point here is, that we will in .config() state store reference to $stateProvider and use it in .run() phase, once the JSON could be loaded via $http
// ref to provider, to be configured later
var $stateProviderRef;
// config phase
app.run(['$stateProvider',
function ($stateProvider)
{
$stateProviderRef = $stateProvider
}
// run phase
app.run(['$q', '$rootScope', '$state', '$http',
function ($q, $rootScope, $state, $http)
{
$http.get("myJson.json")
.success(function(data)
{
angular.forEach(data, function (value, key)
{
var state = {
"url": value.url,
...
};
...
// here we still configure provider, but in RUN
$stateProviderRef.state(value.name, state);
});
But there are some disadvantages. The main is, that direct url navigation (copy - paste) won't be working. The URL will not be rosolved soon enough...
Secondly, my preferred way - create JSON as variable on a server, load it as a script.
So server will somehow generate response via /api/stateData like:
var stateData = [{ stateName : "State1",
...
}];
And we will inject that into page as a resource
<script src="/api/stateData" ...
This could be directly used in .config() phase, and will solve issue with URL being configured soon enough.

Calling angular services globally

I have a set of services which represent a backend logic, that is called by different angular controllers.
During development however I want to call these services directly, in order to test them in isolation, from the browser Javascript console.
How this can be done?
Say there is
app.service('service1', function() {
this.sayHello = function() {
return "Hello"
};
});
Now from Javascript console,
app.somethingToGetService('service1').sayHello()
?
You can get injector for module and from it get service:
angular.injector(['ng', 'myApp']).get('service1').sayHello()

How to access server side configuration in $log decorator

I have have the following routing - this is the part of the home (main page) :
templateUrl: 'pages/home.html',
controller: 'HomeCtrl',
resolve: {
myConfig: function (appConfig) {
return appConfig.retreiveConfig();
}
}
The flow is as follows, I load configuration from server before the main page loads so I can use it there.
In addition I use decorator for the '$log' service,
Now to the question: How can I use the configuration loaded in the routing resolve inside my $log decorator ?
I don't mind to use some default configuration in the decorator until the proper config will arrive from the server.
I can't seem to find a way to use any service that uses $http or $resource because of Circular dependency
Is there any way to manually/problematically initialize the decorator ?
What you can try to do is implement your appConfig.retrieveConfig service method in a way that the config data is cached. Then call the appConfig.retreiveConfig() in your decorator also
In decorator then
appConfig.retreiveConfig().then(function(data) {
//Update your decorator configuration
});
You retreiveConfig should resolve the configuration once, so the service method would look something like
var config;
serviceObj.retreiveConfig=function() {
var defer=$q.defer();
if(config) { defer.resolve(config);}
someRemoteCall().then(function(data)) {
config=data;
defer,resolve(config);
}
return defer.promise;
}

Is it possible to use RestAngular.setBaseUrl for two api access points?

Is it possible to work with Restangular with 2 different APIs?
I would like to have setBaseUrl() for both.
just create two or more Restangular service and config them as you want and inject your module which one you want to use...
UPDATE
this code from restangular github page
// Global configuration
app.config(function(RestangularProvider) {
RestangularProvider.setBaseUrl('http://www.global.com');
RestangularProvider.setRequestSuffix('.json');
});
//First Restangular Service
app.factory('FirstRestangular', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('http://www.first.com');
});
});
//Second Restangular Service
app.factory('SecondRestangular', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('http://www.second.com');
});
});
instead of global config (although you can still set global config for shared properties) create Restangular factories like these and inject them your controller...
// Let's use them from a controller
app.controller('MainCtrl', function(Restangular, FirstRestangular, SecondRestangular) {
// GET to http://www.google.com/users.json
// Uses global configuration
Restangular.all('users').getList()
// GET to http://www.first.com/users.json
// Uses First configuration which is based on Global one, therefore .json is added.
FirstRestangular.all('users').getList()
// GET to http://www.second.com/users.json
// Uses Second configuration which is based on Global one, therefore .json is added.
SecondRestangular.all('users').getList()
});
#wickY26 has the right answer, but I wanted to add something important.
If you are going to minify your code, you need to use inline annotation like so:
app.factory('FirstRestangular', [ 'Restangular', function(Restangular) {
return Restangular.withConfig(function(RestangularConfigurer) {
RestangularConfigurer.setBaseUrl('http://www.first.com');
});
}]);
Note the square brackets.

Resources