Execute $http call in angular .run() finished after controller init - angularjs

I am trying to make an $http call in order to store a JSON into $rootScope in the .run() function in an Angular app but the call is done after the controllers are loaded and I cannot use any of the data from the call. Can anyone help me with this issue?
Also tried to see what happens with console.log, the output is "b" then "a" and for $rootScope.xml is undefined
var app = angular.module('lobby', ['ngRoute']);
app.run(['$rootScope','$http', function($rootScope, $http) {
$http.get(Main.constants.BASEURL+'x.xml').success(function(xml) {
$rootScope.xml = $.xml2json(xml.data);
console.log('a');
});
}]);
app.controller('CategoriesController', function($rootScope) {
console.log($rootScope.xml);
console.log('b');
});

You can try placing a $watch on $rootScope.xml and listen for a new value, which should occur when the $http call resolves & xml scope variable value gets changed.
Code
app.controller('CategoriesController', function($rootScope) {
$rootScope.$watch('xml', function(newValue, oldValue) {
if($rootScope.xml) {
console.log($rootScope.xml);
}
});
});

Related

How to get data from scope by rootscope

I got an app.run where i get my data from an webintent. In the app.run i'm not allowed to use $scope so i use $rootscope.Now my $scope got $scope.sendURL(Object) and I want to make this call inside my app.run.
I searched a lot on stackoverflow and google and I came acros $broadcast and $emit. But i can't figure out if that is right thing to do. Does somebody know what i should try?
edit:
I need to call the $scope.sendURL inside app.run and i have no idea how to do it. I searched on stackoverflow and came acros $broadcast and $emitbut i'm not use if i need to use them.
Move the logic into a factory and use the factory in run and in your controller:
var app = angular.module('myApp', []);
app.run(function (myFactory) {
var data = {};
myFactory.sendURL(data);
});
app.controller('MyController', function ($scope, myFactory) {
$scope.sendURL = myFactory.sendURL;
});
app.factory('myFactory', function ($http) {
return { sendURL : sendURL };
function sendURL(data) {
// Put the logic here
console.log(data);
}
});

Make data from factory http.get accessible to entire controller AngularJS

I am trying to give access to a json file that contains config information for my project (things like rev number, project name, primary contact, etc) I created a factory that retrieves the json file using http.get, I can then pull that data into my controller but I am unable to access it from anywhere in the controller.
I did not write the factory, I found it as an answer to another person's question and it is copied almost entirely so if it not the right way to accomplish what I am trying to do please correct me.
here is the factory:
app.factory('configFactory', ["$http", function($http) {
var configFactory = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('assets/json/config.json').then(function(response) {
// The then function here is an opportunity to modify the response
console.log(response.data.config);
// The return value gets picked up by the then in the controller.
return response.data.config;
});
// Return the promise to the controller
return promise;
}
};
return configFactory;
}]);
and here is my controller:
app.controller('footerController', ['$scope', '$rootScope', 'configFactory', function footerController($scope, $rootScope, configFactory) {
var body = angular.element(window.document.body);
$scope.onChange = function(state) {
body.toggleClass('light');
};
configFactory.async().then(function(d) {
$scope.data = d;
// this console log prints out the data that I am trying to access
console.log($scope.data);
});
// this one prints out undefined
console.log($scope.data);
}]);
So essentially I have access to the data within the function used to retrieve it but not outside of that. I can solve this with rootScope but I am trying to avoid that because I think its a bandaid and not a proper solution.
Any help would be great but this is my first experience with http.get and promises and all that stuff so a detailed explanation would be very much appreciated.
[EDIT 1] The variables from the config file will need to be manipulated within the web app, so I can't use constants.
Don't assign your response data to scope variable , create a property in your factory itself and assign the response to this property in your controller when your promise gets resolved.This way you will get the value in all the other controllers.
I have updated your factory and controller like below
app.factory('configFactory', ["$http", function($http) {
var configFactory = {
async: function() {
// $http returns a promise, which has a then function, which also returns a promise
var promise = $http.get('assets/json/config.json').then(function(response) {
// The then function here is an opportunity to modify the response
console.log(response.data.config);
// The return value gets picked up by the then in the controller.
return response.data.config;
});
// Return the promise to the controller
return promise;
},
config:'' // new proprety added
};
return configFactory;
}]);
app.controller('footerController', ['$scope', '$rootScope', 'configFactory', function footerController($scope, $rootScope, configFactory) {
var body = angular.element(window.document.body);
$scope.onChange = function(state) {
body.toggleClass('light');
};
configFactory.async().then(function(d) {
// $scope.data = d;
configFactory.config=d;
// this console log prints out the data that I am trying to access
console.log($scope.data);
});
// this one prints out undefined
console.log($scope.data);
}]);
Have you looked into using angular constants? http://ilikekillnerds.com/2014/11/constants-values-global-variables-in-angularjs-the-right-way/ You can leverage them as global variables accessible from any controller without the ramifications of assigning the values to rootScope

How to make controller wait untill app.run finishes

In my application, I'm getting some data in app.run using $http.get()
The result from this is required to proceed to the controller.
Currently my controller is executing before the completion of this $http.get
How can I make my controller's execute after the execution of $http.get()
app.run(function ($rootScope, $http, $cookies) {
var currentLanguageId = angular.fromJson($cookies.get('mykey')).UserInfo.Country;
$http.get('myurl').then(function (serviceInfo) {
$rootScope.serviceURL = serviceInfo.data.serviceURL;
}, function (error) {
alert('error service info');
});
run can't handle asynchronous work at the moment. See this issue : https://github.com/angular/angular.js/issues/4003
You have multiple solutions for that. You can get your data without Angular and start Angular manually ( How to wait for a promise in a Run block in Angular? )
Or you can use the resolve of your route to do this : AngularJS : Initialize service with asynchronous data
You can use angular scope event. When data is fetched, you can broadcast an event from $rootScope to all controllers and receive this event in target controller.
You may use $rootScope.$broadcast();in the app.run and then use $rootScope.$on() in your controller.
app.run(function ($rootScope, $http, $cookies) {
var currentLanguageId = angular.fromJson($cookies.get('mykey')).UserInfo.Country;
$http.get('myurl').then(function (serviceInfo) {
$rootScope.serviceURL = serviceInfo.data.serviceURL;
$rootScope.$broadcast('serviceInfoReceived')
}, function (error) {
alert('error service info');
});
});
In your Controller
app.controller ('myCtrl' , function($rootScope) {
$rootScope.$on("serviceInfoReceived", function(){
console.log($rootScope.serviceURL)
});
})
Hope this may help you.
This is an old question, however the $rootScope.$on solution will not always work. It will depend on the timing of the controller registering the listener. The way I have found that works is to set a $rootScope property, and then configure a recursive timeout to wait for it to be set.
function waitForRun() {
if($rootScope.runFinalized){
// do something
} else {
$timeout(waitForRun, 500)
}
}
waitForRun();
and after the last .run block:
.run(function($rootScope) { $rootScope.runFinalized = true; })
Ugly, but it works.

Can not figure out how to store $rootScope in angular.bootstrap

I'm trying to call a web service in AngularJS bootstrap method such that when my controller is finally executed, it has the necessary information to bring up the correct page. The problem with the code below is that of course $rootScope is not defined in my $http.post(..).then(...
My response is coming back with the data I want and the MultiHome Controller would work if $rootScope were set at the point. How can I access $rootScope in my angular document ready method or is there a better way to do this?
angular.module('baseApp')
.controller('MultihomeController', MultihomeController);
function MultihomeController($state, $rootScope) {
if ($rootScope.codeCampType === 'svcc') {
$state.transitionTo('svcc.home');
} else if ($rootScope.codeCampType === 'conf') {
$state.transitionTo('conf.home');
} else if ($rootScope.codeCampType === 'angu') {
$state.transitionTo('angu.home');
}
}
MultihomeController.$inject = ['$state', '$rootScope'];
angular.element(document).ready(function () {
var initInjector = angular.injector(["ng"]);
var $http = initInjector.get("$http");
$http.post('/rpc/Account/IsLoggedIn').then(function (response) {
$rootScope.codeCampType = response.data
angular.bootstrap(document, ['baseApp']);
}, function (errorResponse) {
// Handle error case
});
});
$scope (and $rootScope for that matter) is suppose to act as the glue between your controllers and views. I wouldn't use it to store application type information such as user, identity or security. For that I'd use the constant method or a factory (if you need to encapsulate more logic).
Example using constant:
var app = angular.module('myApp',[]);
app.controller('MainCtrl', ['$scope','user',
function ($scope, user) {
$scope.user = user;
}]);
angular.element(document).ready(function () {
var user = {};
user.codeCampType = "svcc";
app.constant('user', user);
angular.bootstrap(document, ['myApp']);
});
Note Because we're bootstrapping the app, you'll need to get rid of the ng-app directive on your view.
Here's a working fiddle
You could set it in a run() block that will get executed during bootstrapping:
baseApp.run(function($rootScope) {
$rootScope.codeCampType = response.data;
});
angular.bootstrap(document, ['baseApp']);
I don't think you can use the injector because the scope isn't created before bootstrapping. A config() block might work as well that would let you inject the data where you needed it.

How to make sure $http.get in service completes before directive is initialized with data?

How do I make it such that all the data "MyService" needs to retrieve is retrieved before my 'control' directive is created or some function to actually add those items is called? If not, is there some other recommended way involving controllers, etc. Have yet to see a basic example of similar sequence. Note that data may have more values added to it via other functions, in which a method would have to be called explicitly for those new data elements.
Service:
// var app = angular.module...
app.service('MyService', function($http) {
this.data = [];
// Called once to first initalize the data
$http.get('data.json').then(function data(response) {
this.data.push(response);
}
})
Directive (with its own isolate controller? or point to the main app controller)?:
myDirective.directive('control', ['Params', 'MyService', function(Params, MyService) {
// Call on a method "addData(MyService.data) when the data is actually loaded
}]);
I don't think there is any way to stop directive form initiating and wait for a async function call of a service. However, several alternatives here:
1. Use ng-route and set up a resolve with $routeProvider.
API: http://docs.angularjs.org/api/ngRoute.$routeProvider
Basically, you return a promise in a route's resolver, the route's view will wait for that promise to resolve. After resolve, it will load the view and initiate whatever controller or directive within the view.
2. $watch the data property of MyService.
When data changes, do whatever you want
$scope.$watch(function() {
return MyService.data;
}, function(newVal, oldVal) {
// do something
}, true);
3. Trigger a ready event on MyService, listen to MyService in your directive.
This requires you to include some event library such as 'EventEmitter`, and mix it into MyService.
4. $broadcast ready event on $rootScope from MyService, and listen to it in your directive.
app.service('MyService', function($http, $rootScope) {
this.data = [];
var _this = this;
// Called once to first initalize the data
$http.get('data.json').then(function data(response) {
_this.data.push(response);
$rootScope.$broadcast('MyServiceReady');
}
})
myDirective.directive('control', ['Params', 'MyService', function(Params, MyService) {
return function(scope) {
scope.$on('MyServiceReady', function() {
// do something
});
};
}]);

Resources