Creating a promise within resolve in routes for Angular app - javascript

I am currently working on an Angular app, but I am having difficulty implementing a promise with resolve. What I want to accomplish is in the following:
Get a users geolocation
Use the users geolocation as parameters for an API call to SongKick
After the data has been received from the API call successfully I want the home.html page to load with the data found in q.resolve
All want all of this to happen in order. Essentially, there is data I need to obtain before displaying my home page. The problem is that when I console log getLocation in my homeCtrl it is undefined. Anyone know why or have a better way to approach this kind of thing?
FYI:assignValues is a success callback after geolocation values have been defined.
routes.js
angular.module('APP', ['ui.router',
'APP.home',
'uiGmapgoogle-maps'
])
.config(function($urlRouterProvider, $stateProvider, uiGmapGoogleMapApiProvider) {
$stateProvider.state("home", {
url:"/",
templateUrl: '/home.html',
controller: 'homeCtrl',
resolve: {
getLocation: function(dataFactory, $q){
var q = $q.defer();
navigator.geolocation.getCurrentPosition(assignValues);
function assignValues(position) {
dataFactory.getMetroArea(position.coords.latitude, position.coords.longitude).then(function(data){
q.resolve(data);
return q.promise;
})
}
}
}
})
HomeCtrl.js
angular.module('APP.home',['APP.factory'])
.controller('homeCtrl', ['$rootScope', '$scope', '$http', '$location', 'dataFactory', 'artists','uiGmapGoogleMapApi', 'getLocation', homeCtrl])
function homeCtrl($rootScope, $scope, $http, $location, dataFactory, artists, uiGmapGoogleMapApi, getLocation){
$scope.googleMapsData = getLocation
}
dataFactory.js(left out rest of factory)
dataFactory.getMetroArea = function(lat, lon){
return $http.get('http://api.songkick.com/api/3.0/search/locations.json?location=geo:'+ lat + ',' + lon + '&apikey=APIKEY')
}

Resolve methods need to either return a promise, or actual data. Here's a cleaned up resolve method which include rejections (you don't want to leave your request hanging).
angular.module('APP', ['ui.router', 'APP.home', 'uiGmapgoogle-maps'])
.config(function($urlRouterProvider, $stateProvider, uiGmapGoogleMapApiProvider) {
$stateProvider.state("home", {
url: "/",
templateUrl: '/home.html',
controller: 'homeCtrl',
resolve: {
getLocation: function(dataFactory,$q) {
var q = $q.defer();
navigator.geolocation.getCurrentPosition(function(position){
dataFactory.getMetroArea(position.coords.latitude, position.coords.longitude).then(function(data){
q.resolve(data);
},function(err){
q.reject(err);
})
},function(err){
q.reject(err);
});
return q.promise;
}
}
});
});

I think your getLocation function should be
getLocation: function(dataFactory, $q){
var q = $q.defer();
navigator.geolocation.getCurrentPosition(assignValues);
function assignValues(position) {
dataFactory.getMetroArea(position.coords.latitude, position.coords.longitude)
.then(function(data){
q.resolve(data);
});
}
return q.promise;
}

Related

UI router resolve do not work

Right now i am trying to make an Angular JS install application, to install a CMS. So i am trying to block access to a state (ui router), i am doing it with a resolve function. But the problem is, that i make a get request to an API, which returns true or false, and the resolve function do not wait for the get request to complete, so it just loads the state.
Here is my code:
app.run(['$rootScope', '$http', function($rootScope, $http) {
$rootScope.$on('$stateChangeStart', function() {
$http.get('/api/v1/getSetupStatus').success(function(res) {
$rootScope.setupdb = res.db_setup;
$rootScope.setupuser = res.user_setup;
});
});
}]);
app.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/404");
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
data: function($q, $state, $timeout, $rootScope) {
var setupStatus = $rootScope.setupdb;
var deferred = $q.defer();
$timeout(function() {
if (setupStatus === true) {
$state.go('setup-done');
deferred.reject();
} else {
deferred.resolve();
}
});
return deferred.promise;
}
}
})
.state('user-registration', {
url: "/install/user-registration",
templateUrl: "admin/js/partials/user-registration.html",
controller: "RegisterController"
})
.state('setup-done', {
url: "/install/setup-done",
templateUrl: "admin/js/partials/setup-done.html"
})
.state('404', {
url: "/404",
templateUrl: "admin/js/partials/404.html"
});
}]);
Here you can see a timeline for the loading of the page:
Here you can see what the API returns:
Your db-install resolver function needs to chain from the $http.get for install status.
$stateProvider.state('db-install', {
url: "/install/db",
templateUrl: 'admin/js/partials/db-install.html',
controller: 'DBController',
resolve: {
data: function($state, $http) {
return $http.get('/api/v1/getSetupStatus'
).then (function(result) {
var setupdb = result.data.db_setup;
var user_setup = result.data.user_setup;
//return for chaining
return setupdb;
}).then (function (setupStatus) {
//use chained value
if (setupStatus === true {
//chain with $state.go promise
return $state.go('setup-done');
} else {
//resolve promise chain
return 'setup-not-done';
};
})
}
}
})
By returning and chaining from the status $http.get, the resolver function waits before executing (or not executing) the $state.go.
For more information on chaining promises, see the AngularJS $q Service API Reference -- chaining promises.
The call to getSetupStatus gets executed in the $stateChangeStart so resolve is not aware that it has to wait. You can put the $http call inside of the resolve function, like this:
function($q, $state, $timeout) {
return $http.get('/api/v1/getSetupStatus')
.then(function(res) {
if(res.db_setup) {
$state.go('setup-done');
}
else {
return true;
}
});
}
By making the resolve parameter return a callback the state will load after the promise is resolved.

Angular load route when ajax returns

How can I delay/defer a route/controller until an anonymous function returns? On app bootstrap, I default rootScope.me as guest account until it can check cookies for a logged in user.
I have a controller, testCtrl, that relies on rootScope.me data to load appropriate user data. The controller is fired before rootScope.me has a chance to be set to the user.
I know Angular has $q service for resolving promises, but am not sure how to apply this to routing.
angular
.module('DDE', [])
.run(['$rootScope', 'Me', function($rootScope, Me) {
$rootScope.me = {
username : 'Guest',
id : -1
};
if (Cookies.get('user_id') && Cookies.get('username')) {
Me.getProfile({user_id : Cookies.get('user_id')}).success(function (res) {
$rootScope.me = res;
}).error(function (err) {
console.log('Error: ', err);
});
}
}])
.config(['$routeProvider', '$httpProvider', '$authProvider',
$routeProvider.
when('/test', {
templateUrl: '/html/pages/test.html',
controller: 'testCtrl'
}).
.config(['$routeProvider', '$httpProvider', '$authProvider', '$stateProvider', '$urlRouterProvider',
function($routeProvider, $httpProvider, $authProvider, $stateProvider, $urlRouterProvider) {
//Cannot inject services like Me or $rootScope as I need
function loadProfile () {
Me.getProfile({user_id : Cookies.get('user_id')}).success(function (res) {
$rootScope.me = res;
}).error(function (err) {
console.log('Error: ', err);
});
}
$stateProvider.
state('test', {
url : '/test',
templateUrl: '/html/pages/test.html',
controller : 'testCtrl',
resolve : {
ProfileLoaded : function () {
return loadProfile();
}
}
});
edit: adding angular's ngRoute example.
You can look into ui-router's resolve. It basically waits for your promise to be resolved before loading/navigating to your state/route.
documentation
Each of the objects in resolve below must be resolved (via
deferred.resolve() if they are a promise) before the controller is
instantiated. Notice how each resolve object is injected as a
parameter into the controller.
Here's angular's ngRoute example from angular's documentation:
.config(function($routeProvider, $locationProvider) {
$routeProvider
.when('/Book/:bookId', {
templateUrl: 'book.html',
controller: 'BookController',
resolve: {
// I will cause a 1 second delay
delay: function($q, $timeout) {
var delay = $q.defer();
$timeout(delay.resolve, 1000);
return delay.promise;
}
}
})

AngularJS load controller after resolve doesn't work

Goal:
In my application every controller should be initialized after a user has a session / is logged in, because in this controller I use the data of logged in user.
Code:
app.js
app.run(function($q, $rootScope, AuthSvc){
$rootScope.ajaxCall = $q.defer();
AuthSvc.reloadSession().then(
function(response){
if(response!=null && response!=undefined){
$rootScope.activeUserSession = response;
$rootScope.ajaxCall.resolve();
}else{
$rootScope.activeUserSession = null;
$rootScope.ajaxCall.reject();
}
});
return $rootScope.ajaxCall.promise;
});
routes.js
.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/timeTracking', {
templateUrl: 'partials/timeTracking/projectView.html',
controller: 'timeTrackingController',
resolve: {
response: function($rootScope, $q) {
var defer = $q.defer();
$rootScope.ajaxCall.promise.then(
function(){
defer.resolve();
return defer.promise;
});
}
}
}).
Problem: Controller gets initialized sometimes before the user has a session, I do not understand why.
Sorry I am new to Angular and my english is also crap, so I hope nevertheless you can understand what is my problem.
I think placing your session reload into the app.run is not the right place. Add it directly to resolve and checkout the docs for $q to learn how promises are working.
Because you can't call a promise or defer. You need to call a function that's returning a promise then you can add your then method to do your stuff after the promise is resolved.
Please have a look at the demo below or here at jsfiddle.
It's just an asynchronous method with $timeout to simulate the auth because I don't have a backend to add in the demo.
You can add your AuthSvc directly into resolve.
angular.module('demoApp', ['ngRoute'])
.controller('timeTrackingController', function($scope, response) {
$scope.data = response;
})
.factory('authService', function($q, $timeout) {
return {
reloadSession: function() {
var deferred = $q.defer();
$timeout(function() {
// just simulate session reload
// real api would to the job here
console.log('resolved defer now!!');
deferred.resolve({dummyData: 'hello from service'});
}, 1000);
return deferred.promise;
}
}
})
.config(['$routeProvider',
function($routeProvider) {
$routeProvider.
when('/timeTracking', {
templateUrl: 'partials/timeTracking/projectView.html',
controller: 'timeTrackingController',
resolve: {
response: function(authService) {
return authService.reloadSession().then(function(data) {
return data;
})
}
}
})
.otherwise('/timeTracking');
}]);
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.3/angular-route.js"></script>
<div ng-app="demoApp">
<script type="text/ng-template" id="partials/timeTracking/projectView.html">
project view: {{data | json}}
</script>
<div ng-view=""></div>
</div>

Angular route resolve not waiting for promise

I'm new to angular and I have a user route which I'm attempted to resolve the user object for before rendering the view. I've injected $q and deferred the promise, however, the view is still loading before the promise is returned.
Route:
.when('/user/:userId', {
templateUrl: 'user/show.html',
controller: 'UserController',
resolve: {
user: userCtrl.loadUser
}
})
Controller
var userCtrl = app.controller('UserController', ['$scope',
function($scope){
$scope.user = user; // User is undefined
// This fires before the user is resolved
console.log("Fire from the controller");
}]);
userCtrl.loadUser = ['Restangular', '$route', '$q',
function(Restangular, $route, $q) {
var defer = $q.defer();
Restangular.one('users', $route.current.params.userId).get().then(function(data) {
console.log("Fire from the promise");
defer.resolve(data);
});
return defer.promise;
}];
After looking through the Github issues, I found a similar problem and resolved it with the following:
userCtrl.loadUser = ['Restangular', '$route',
function(Restangular, $route) {
return Restangular.one('users', $route.current.params.userId).get();
}];

How to make ng-view wait for XHR response?

I´m trying to make the ng-view wait for a xhr request. I have two controllers for a routed ng-view, the first one is loaded perfectly. But the other doesn't gets rendered well, because the xhr response happens after partial.html is downloaded. How do I avoid the partial.html request until that client get the xhr response?
You can see below the code for the route configuration:
var configuration = [
'$routeProvider',
'$locationProvider',
function(routeProvider, locationProvider) {
routeProvider.when('/', {
templateUrl: '/partials/hotelinfo.html',
controller: 'HotelInfo'
}).when('/service/dept/:id', {
templateUrl: '/partials/department.html',
controller: 'Department'
}).otherwise({
redirectTo: '/'
});
locationProvider.html5Mode(true);
}
];
Below you can see the controller configuration that gets the xhr response
<!-- language: lang-js -->
var Department = [
'$scope',
'$routeParams',
function (scope, routeParams) {
http.get('/service/dept/' + routParams.id).success(function (data) {
scope.data = data;
});
}
];
Instead of calling $http.get from your controller, call it from a resolve function on $routeProvider and inject it into the controller. That will cause Angular to not load your view until the promise from $http is resolved.
You can accomplish this using resolve in the routeProvider. It returns a promise. The view will not load until that promise is resolved. You can resolve that promise in your controller.
See http://docs.angularjs.org/api/ngRoute/provider/$routeProvider for more info.
var configuration = [
'$routeProvider',
'$locationProvider',
function(routeProvider, locationProvider) {
routeProvider.when('/', {
templateUrl: '/partials/hotelinfo.html',
controller: 'HotelInfo'
}).when('/service/dept/:id', {
template: '/partials/department.html',
controller: 'Department',
resolve: {
deferred: function($q) {
return $q.defer();
}
}
}).otherwise({
redirectTo: '/'
});
locationProvider.html5Mode(true);
}
];
var Department = [
'$scope',
'$routeParams',
'deferred',
function (scope, routeParams, deferred) {
http.get('/service/dept/' + routParams.id).success(function (data) {
scope.data = data;
deferred.resolve();
});
}
];

Categories

Resources