How use async code inside app.run? - javascript

I have a run block that is querying my server to check if the user is authenticated.
.run(function($http, userService){
var base_url = 'http://server.com:3000';
$http.get(base_url + '/users/isloggedin')
.success(function(data, status, headers, config){
userService.setUserData(data.userData);
userService.setIsUserLoggedIn(true);
});
})
Later, I have another run that will require info from the first run block. The problem with this is that my run code has async code and I am not getting the true value at the first time to userService.getIsUserLoggedIn().
How can I tell angularjs to execute the second run block only after the first one has been completed?
The second run block:
.run(function($rootScope, $location, $state, userService){
//Run to handle authentication
var authOnly = ['/painel'];
var unAuthOnly = ['/home'];
var checkAuthRoute = function(url){
var exist = authOnly.indexOf(url);
return exist > -1;
};
var checkUnAuthRoute = function(url){
var exist = unAuthOnly.indexOf(url);
return exist > -1;
};
$rootScope.$on('$stateChangeStart', function(evt, toState, toParams, fromState, fromParams){
console.log(toState.url + ' ' + fromState.url + ' - ' + userService.getIsUserLoggedIn());
if(!userService.getIsUserLoggedIn() && checkAuthRoute(toState.url)){
console.log('Aqui..');
evt.preventDefault();
$state.go('login');
}
});
})
Thanks

You can use callbacks to make chain async requests in Javascript. Something like this might work:
.run(function($http, $rootScope, $location, $state, userService){
var base_url = 'http://server.com:3000';
$http.get(base_url + '/users/isloggedin')
.success(function(data, status, headers, config){
userService.setUserData(data.userData);
userService.setIsUserLoggedIn(true);
handleAuth($rootScope, $location, $state, userService);
});
})
And define this function before the .run code from above:
function handleAuth($rootScope, $location, $state, userService){
var authOnly = ['/painel'];
var unAuthOnly = ['/home'];
var checkAuthRoute = function(url){
var exist = authOnly.indexOf(url);
return exist > -1;
};
var checkUnAuthRoute = function(url){
var exist = unAuthOnly.indexOf(url);
return exist > -1;
};
$rootScope.$on('$stateChangeStart', function(evt, toState, toParams, fromState, fromParams){
console.log(toState.url + ' ' + fromState.url + ' - ' + userService.getIsUserLoggedIn());
if(!userService.getIsUserLoggedIn() && checkAuthRoute(toState.url)){
console.log('Aqui..');
evt.preventDefault();
$state.go('login');
}
});
}
Or a much more popular alternative (to prevent constant chaining of callbacks which makes code unreadable--aka the Pyramid of Doom), is to use Promises.
A promise takes an async function and returns a promise, which you can use to chain requests (ex. the $http method returns a promise that you're using called success). It is not available in ECMAScript 5, but will be in 6. People have made a bunch of implementations of Promises, such as Kris Kowal's Q, and Angular has a stripped down version of this library called $q.

Because this is async call, technically first block is finished, but the call(s) inside it wasn't. The only thing that comes to my mind is adding promise into userService and communicate through it:
.run(function($http, $q, userService){
var base_url = 'http://server.com:3000';
var deferred = q.defer();
$http.get(base_url + '/users/isloggedin')
.success(function(data, status, headers, config){
userService.setUserData(data.userData);
userService.setIsUserLoggedIn(true);
deferred.resolve();
});
userService.setPromise(deferred.promise);
})
And in second run:
.run(function(userService){
userService.getPromise().then(function(){
//code that requires first run to finish
});
})
But if somewhere you need code from second run to finish(I mean do smth only after second run), it will require same structure again, and that's not really good, so you need to change the logic.

Related

Set Angularjs Service data to Controller Variable

I am trying to set the controllers scope variable to the data, however no luck. The console.log in the controller shows undefined. Appreciate the help!
My angular service has the following code --
service('VyrtEventService', ['$http', function($http) {
var events = [];
this.getArtistEvents = function(artistId) {
var url = '/api/users/' + artistId + '/';
var promise = $http.get(url).success(function(data, status, headers, config) {
events = data.artist.events;
console.log(events);
return events;
}).catch(function(error) {
status = 'Unable to load artist data: ' + error.message;
console.log(status);
});
return promise;
};
}]);
And I am referencing it in the controller as follows --
VyrtEventService.getArtistEvents($scope.artistId).then(function(data){
$scope.events = data.data.artist.events;
});
console.log($scope.events);
You should just set $scope.events = data in your controller cause your promise already returns data.artist.events when it resolves
To pass scope to service from anywhere in controller. Make sure you inject service .
controllersModule.controller('MyCtrl', function($scope, $filter, $http, $compile, ngTableParams, **FactoryYouwant**)
{
**FactoryYouwant**.getdata($scope.**scopeYoutwantTopass**).then (function(responseData){
var ResponseFromServer =responseData.data;
}
in service
controllersModule.factory('**FactoryYouwant**, function($http) {
var responseData = null;
return {
getdata : function(**data**){ (you dont have to use $)
responseData = $http.post or whatever actually gets you data;
return responseData;
}
};
});
I hope this helps you to call get data from service anywhere in controller.

Angular app only shows adjusted dynamic content after refresh

I'm using a service(factory) to access my data on the server side, I'm using $routeParams to include the id to the request to the server. And this all works perfectly fine, but only the first time I run through the program, here is some of my code:
my controller:
klusplan.success(function(data) {
console.log("onsucces",data);
$scope.klusplan = data[0];
$scope.klusplan.duratie = localStorage.getItem('Duration');
$scope.klusplan.orderId = localStorage.getItem('klusplan');
var tijdInUren = localStorage.getItem('Duration').split(":");
var Uren = tijdInUren[0];
var Minuten = tijdInUren[1];
Uren = Uren + (Minuten / 60);
$scope.klusplan.kosten = ($scope.klusplan.vm_kl_tarief * Uren);
});
my service:
app.factory('klusplan', ['$http', '$routeParams', function($http, $routeParams) {
return $http.get('http://localhost:8080/getdata/klusplan.php?id='+$routeParams.klplan_id)
.success(function(data, status) {
console.log("succes", data, status);
return data;
})
.error(function(err) {
return err;
});
}]);
and last but not least the route
.when('/klusplan/:klplan_id', {
controller: 'KlusplanController',
templateUrl: 'views/klusplan.html'
})
I've already tried some things like:
$httpProvider.defaults.headers.get['If-Modified-Since'] = 'Mon, 26 Jul 1997 05:00:00 GMT';
$httpProvider.defaults.headers.get['Cache-Control'] = 'no-cache';
$httpProvider.defaults.headers.get['Pragma'] = 'no-cache';
and
app.run(function($rootScope, $templateCache) {
$rootScope.$on('$routeChangeStart', function(event, next, current) {
console.log("event: ", event," next: ", next," current: ", current)
$templateCache.remove(current.templateUrl);
});
});
Now the voodoo all starts when I run through my application for the second time, instead of getting the data from the server, the http request never fires. Instead it shows me the data gathered from the last time I ran through it.
I would appreciate any and all help.
You are calling the klusplan.success function directly. So you will always get the already fetched data.
If you write the factory like this:
app.factory('klusplan', ['$http', '$routeParams', function($http, $routeParams) {
return {
getData: function() {
return $http.get('http://localhost:8080/getdata/klusplan.php?id='+$routeParams.klplan_id);
}
};
}]);
And then inside the controller:
klusplan.getData().then(function(data) { ...
So you call everytime $http.get and getting new Data from the server instead of just the already successful fetched data.

Getting the object obtained from asynchronous $http service in angularJS to be accessible globally in $scope

I am working using Angular JS. I am trying to get a json object obtained using $http service to be accessible in a $scope variable. Inside all the asynchronous AJAX ($http.get()) calls, if I try to print the data obtained stored inside a $scope variable and print it, it works successfully and shows me the expected data. But outside scope of the asynchronous method, the same $scope variable assigned with data obtained loses hold of it and prints undefined.
Code:
var app = angular.module('chariot', ['ngRoute', 'ngFileUpload']);
app.factory('getTestcaseFactory', ['$http', '$routeParams', '$q', function($http, $routeParams, $q) {
return {
list: function(){
var deferred = $q.defer();
$http.get('/testcase/' + $routeParams.testcase)
.success(function(data, status, headers, config) {
deferred.resolve(data);
})
.error(function(data, status, headers, config) {
deferred.reject("Error fetching XML file: " + status + ' ' + JSON.stringify(headers));
});
return deferred.promise;
}
};
}
]);
app.controller('testcaseCapCtrl', ['$scope', '$routeParams', '$http', 'getTestcaseFactory', function($scope, $routeParams, $http, getTestcaseFactory) {
$scope.myjsonobj = '';
var fetchTestcaseDetails = function() {
getTestcaseFactory.list()
.then(
function(data) {
$scope.xml.file = data;
var x2js = new X2JS();
var jsonObj = x2js.xml_str2json($scope.xml.file);
$scope.xml.json = JSON.stringify(jsonObj, null, 2);
$scope.model = jsonObj;
console.log($scope.model);//PRINTS THE RIGHT DATA
},
function(data) {
alert(data);
});
}
fetchTestcaseDetails();
console.log($scope.model); //Prints undefined
}]);
By the time
console.log($scope.model);
executes, the $http request would have not gone through yet, and this is why it prints undefined. Once the $http request is done, your $scope.model will update accordingly. You can test this by using a $timeout
$timeout(function () {
console.log($scope.model);
}, 5000);
Don't forget to inject $timeout in your controller.
This saves my day! $timeout to the rescue. Thanks #Dan Moldovan for the answer! As the $http service is asynchronous, we have to set a timer to wait for a time interval till the point the data is really received in the promise.success section and then it can be assigned to a variable inside $scope.
var app = angular.module('chariot', ['ngRoute', 'ngFileUpload']);
app.factory('getTestcaseFactory', ['$http', '$routeParams', '$q', function($http, $routeParams, $q) {
return {
list: function(){
var deferred = $q.defer();
$http.get('/testcase/' + $routeParams.testcase)
.success(function(data, status, headers, config) {
deferred.resolve(data);
})
.error(function(data, status, headers, config) {
deferred.reject("Error fetching XML file: " + status + ' ' + JSON.stringify(headers));
});
return deferred.promise;
}
};
}
]);
app.controller('testcaseCapCtrl', ['$scope', '$routeParams', '$timeout', 'getTestcaseFactory', function($scope, $routeParams, $timeout, getTestcaseFactory) {
var fetchTestcaseDetails = function() {
getTestcaseFactory.list()
.then(
function(data) {
$scope.xml.file = data;
var x2js = new X2JS();
var jsonObj = x2js.xml_str2json($scope.xml.file);
$scope.xml.json = JSON.stringify(jsonObj, null, 2);
$scope.model = jsonObj;
console.log($scope.model);//PRINTS THE RIGHT DATA
},
function(data) {
alert(data);
});
}
fetchTestcaseDetails();
$timeout(function() {
console.log($scope.model); //Prints the Data now
}, 2000);
}]);
The solutions posted so far are fundemantally wrong, as they depend on an arbitrary timeout, and nothing guarantees that the async answer will be available by then.
As I suggested in a comment above, here are 2 more data/event-driven solutions.
You either only print in the callback function (which you're already doing in your example)
Or since you're using angular anyway, you can set up a watch (which in the background uses a not-too-nice dirty checking solution, but at least it's abstracted away from your code)
If you want to run a function called processData once the data is available, solution 1 you already do in your example, see this line:
console.log($scope.model);//PRINTS THE RIGHT DATA
Here you could call any other function to trigger the continuation of the process, just call whatever function:
console.log($scope.model);//PRINTS THE RIGHT DATA
processData();
Using angular's $watch mechanism (2.):
$scope.$watch(
function(){return $scope.model;},
function(newValue, oldValue){
if(newValue != null){
console.log($scope.model);
processData();
}
})
This will print the data when it is available after the async callback, and the program will continue with processing from then.

undefined function in timeout angularjs

I have the following controller :
app.controller('ListeSASController', function($scope, $rootScope, $routeParams, $location, userService, RefreshSASServices, $timeout){
this.IsUserLogged = function()
{
return userService.user().isLogged;
};
var promise = $timeout(RefreshSASServices.RafraichirSAS(), 100);
this.getSAS = function(){
return RefreshSASServices.getSAS();
};
$scope.$on('$locationChangeStart', function(){
RefreshSASServices.ArreterLesRafraichissements();
});
});
with the following service :
app.service('RefreshSASServices', function($http, userService, serverConfigService, $q, $timeout, $translate, constantsServices) {
var listeSAS = [];
var $this = this;
var promiseRefreshSAS;
// Getters
this.getSAS = function()
{
return listeSAS;
};
//Setters
this.clearDatas = function()
{
listeSAS = [];
};
// Communication with the server
$this.getServerUri = function()
{
return serverConfigService.getServerUri()+"majsvc/";
};
// Fonctions de rafraichissement
$this.ArreterLesRafraichissements = function()
{
if(promiseRefreshSAS !== undefined)
$timeout.cancel(promiseRefreshSAS);
};
$this.GetSASFromServer = function()
{
var promises;
if(userService.user().isLogged)
{
var uri = $this.getServerUri() + "getAllSAS/"+userService.user().UserObject._id;
promises = $http.get(uri)
.success(function(data, status, headers, config) {
// this callback will be called asynchronously
// when the response is available
return data;
}).
error(function(data, status, headers, config) {
// called asynchronously if an error occurs
// or server returns response with an error status.
return "";
});
}else{
promises = $q.when(!userService.user().isLogged)
}
return promises;
};
$this.RafraichirSAS = function () {
// functions that call
$this.GetSASFromServer()
.then(function(promise){
if(promise !== undefined && promise.data !== undefined)
{
listeSAS = promise.data;
//alert('refreshing the SAS list:' + JSON.stringify(listeSAS));
}else listeSAS = [];
promiseRefreshSAS = $timeout($this.RafraichirSAS, 3000);
})
.catch(function(error)
{
console.error("Error :", error);
promiseRefreshSAS = $timeout($this.RafraichirSAS, 7000);
});
};
});
When I load my page using routes :
.when('/listeSAS', {
templateUrl : './includes/sas/liste_sas.html',
controller : 'ListeSASController',
controllerAs : 'controller'
})
everything works fine, if my data changes on the server it gets updated on the UI, My UI is also displaying what I want. Everything is OK except that when the pages loads I get the following error :
TypeError: undefined is not a function
at file:///includes/libs/angular.js:14305:28
at completeOutstandingRequest (file:///includes/libs/angular.js:4397:10)
at file:////includes/libs/angular.js:4705:7
which is the function "timeout" of angular, and the line 14305 is :
try {
deferred.resolve(fn());
} catch(e) {
deferred.reject(e);
$exceptionHandler(e);
}
finally {
delete deferreds[promise.$$timeoutId];
}
Why angular is throwing this exception ? What did I do wrong ?
To be known :
On my login page I set 2 timeouts which I don't stop because they refresh "global" variables such as the number of private messages. Despite the error both timeout are still working.
I use node webkit with my application and it crashes maybe one in three times when I open this route (after 5-10 seconds).
Thank you for your help.
Is it that you're calling RafraichirSAS(), which returns undefined instead of passing in the function?
E.g, instead of
$timeout(RefreshSASServices.RafraichirSAS(), 100);
Do
$timeout(RefreshSASServices.RafraichirSAS, 100);

JS Global Variable in AngularJS returns undefined

I'm trying to access a value inside some Javascript to use in an AngularJS service. I have stored the value inside a variable successfully but I am having issues getting that variable into my AngularJS service. Here is my code:
JS function:
function onNotification(e) {
$("#app-status-ul").append('<li>EVENT -> RECEIVED:' + e.event + '</li>');
switch( e.event )
{
case 'registered':
if ( e.regid.length > 0 )
{
$("#app-status-ul").append('<li>REGISTERED -> REGID:' + e.regid + "</li>");
// Your GCM push server needs to know the regID before it can push to this device
// here is where you might want to send it the regID for later use.
var regid = e.regid
console.log(regid);
}
break;
The variable regid is what I am trying to access.
AngularJS Service:
App.service('regID', function()
{
return{}
});
Angular Function:
App.controller('MainCtrl', function($scope, $state, regID, $window){
console.log('MainCtrl');
var pushNotification;
document.addEventListener("deviceready", onDeviceReady, false);
$scope.regID = regID;
$scope.regID.regID = $window.regid;
console.log($scope.regID);
function onDeviceReady()
{
pushNotification = window.plugins.pushNotification;
$("#app-status-ul").append('<li>registering ' + device.platform + '</li>');
if ( device.platform == 'android' || device.platform == 'Android'){
pushNotification.register(
successHandler,
errorHandler,
{
"senderID":"460885134680",
"ecb":"onNotification",
});
} else {
pushNotification.register(
tokenHandler,
errorHandler,
{
"badge":"true",
"sound":"true",
"alert":"true",
"ecb":"onNotificationAPN"
});
}
}
function successHandler (result, $scope, regID, $window)
{
alert('result = ' + result);
}
function errorHandler (error)
{
alert('error = ' + error);
}
});
Every time I run this $window.regid comes back as undefined. Any ideas as to why?
So you have not assigned it to a global variable. You have assigned it to a local function scope variable.
To assign it to a global variable use window.regID rather than var regID.
Your regID service does nothing. It should return $window.regID.
This all being said, this is not the right approach. The correct approach would be a service that returns a promise. The service also listen to native javascript or jqlite events, and resolve the promise when the event is handled.

Categories