AngularJS - Waiting for an asynchronous call - javascript

I'm starting to learn AngularJS and so far so good but I am currently stuck on a small issue about an synchronous function.
I would like the function AuthenticationSharedService.login() to be called and returned before executing the rest of the code. How can I do that?
app.js
myApp.config(['$routeProvider', '$httpProvider', 'USER_ROLES',
function ($routeProvider, $httpProvider, USER_ROLES) {
$routeProvider
.when('/login', {
templateUrl: 'views/login.html',
controller: 'LoginController',
access: {
authorizedRoles: [USER_ROLES.all]
}
})
.when('/error', {
templateUrl: 'views/error.html',
access: {
authorizedRoles: [USER_ROLES.all]
}
})
.otherwise({
templateUrl: 'views/main.html',
controller: 'MainController',
access: {
authorizedRoles: [USER_ROLES.user]
}
});
}])
.run(['$rootScope', '$location', 'AuthenticationSharedService', 'Session', 'USER_ROLES',
function($rootScope, $location, AuthenticationSharedService, Session, USER_ROLES) {
$rootScope.$on('$routeChangeStart', function (event, next) {
<!-- Bit of code to execute before going further -->
if (!Session.login) {
console.log('First attempt to login');
AuthenticationSharedService.login();
}
<!-- /////////////////////////////////////////// -->
if (AuthenticationSharedService.isAuthenticated()) {
// user is not allowed
$rootScope.$broadcast("event:auth-notAuthorized");
} else {
// user is not logged in
$rootScope.$broadcast("event:auth-loginRequired");
}
});
}]);
authenticationSharedService.js
myApp.factory('AuthenticationSharedService', ['$rootScope', '$http', '$cookieStore', 'authService', 'Session', 'Account',
function ($rootScope, $http, $cookieStore, authService, Session, Account) {
return {
login: function () {
return Account.get(function(data) {
Session.create(data.login, data.roles);
authService.loginConfirmed(data);
});
}
};
}]);

You need to use resolve. See http://docs.angularjs.org/api/ngRoute/provider/$routeProvider or http://www.bfcamara.com/post/66001429506/authentication-in-a-spa-with-angular - this second link is quite good.
Resolve is a map of dependencies that should be injected into the controller. If any of the mapped objects are functions, the functions will be evaluated and their return values injected. If a function returns a promises, the view will not be rendered until the promise is resolved.
Just FYI, the view will not be rendered at all if the promise is rejected. You would want to deal with that situation in $rootScope.$on('routeChangeError', functionToHandleRejection)
Here's an example that should work for you:
.when('someUrl', {
resolve: {
object: function(AuthenticationSharedService) {
return AuthenticationSharedService.login();
}
}
})

Related

Multiple url changes with $routeChangeStart

Hi all I having a hard time figuring this out and ran out of ideas. I have this website which I should restrict some urls if the user has no authentication. I reviewed some questions and found something similar to this. But it is not quite what I wanted it to be.
So I have some link which do not require user authentication but can access the url.
Other link which needs both, access and user authentication.
So this is my app.js looks
angular.module('efutures', [
'ngRoute',
'ngCookies',
'ui.bootstrap',
]).config(['$routeProvider', '$httpProvider', function ($routeProvider, $httpProvider) {
$routeProvider.when('/login', {
templateUrl: 'app/pages/login.html',
controller: 'LoginController',
access: {
allowAnonymous: true //no need auth
}
})
.when('/signup', {
templateUrl: 'app/pages/Signup.html',
controller: 'SignupController',
access: {
allowAnonymous: true //no need auth
}
})
.when('/enteremail', {
templateUrl: 'app/pages/reset pages/reenteremail.html',
controller: 'EmailController',
access: {
allowAnonymous: true //no need auth
}
})
.when('/password-reset', {
templateUrl: 'app/pages/reset pages/password.reset.html',
controller: 'ResetPwdController',
access: {
allowAnonymous: false //needs auth
}
})
.when('/dashboard', {
templateUrl: 'app/pages/dashboard.html',
controller: 'DashboardController',
access: {
allowAnonymous: false //needs auth
}
})
.when('/registration', {
templateUrl: 'app/pages/registration1.html',
controller: 'registationController'
access: {
allowAnonymous: false //needs auth
}
});
$routeProvider.otherwise({
redirectTo: '/login'
});
}]).run(run);
run.$inject = ['$rootScope', '$location', '$cookieStore', '$http'];
function run($rootScope, $location, $cookieStore, $http) {
$rootScope.hasauth = localStorage.getItem('hasauth');
$rootScope.username = localStorage.getItem('username');
$rootScope.$on('$routeChangeStart', function (event, next, current) {
//console.log($rootScope.hasauth);
//console.log(next.access.allowAnonymous);
if (next.access.allowAnonymous || $rootScope.hasauth === null) {
$location.path('/login');
}
else {
event.preventDefault();
}
});
}
So as above explains code, I have 3 pages which doesn't need authentication and can be allowed to access the urls. Also after Logged in I should restrict going back to those 3 pages but should access other remaining ones
the authentication is taken by the loacalStorage hasauth which returns true when logged in and null when not.
How do I approach this? Help is greatly appreciated.

Injecting into lazy loaded AngularJS Controller when using ocLazyLoad

I started using ocLazyload to lazy load few of my AngularJs controllers. I have used it along with the $routeProvider like this
$routeProvider.when("/login", {
templateUrl: "login.html",
resolve: {
loadCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load('LoginCtrl.js');
}]
}
}).
and this works fine.
I have another route definition which uses resolve property to resolve few items before loading the controller.
when("/dashboard", {
templateUrl: "dashboard.html",
controller: "DashboardCtrl",
resolve: {
profileData: getProfile,
count : getCount
}
}).
Now I want to lazy load this controller too, and I tried it like this
when("/dashboard", {
templateUrl: "dashboard.html",
resolve: {
profileData: getProfile,
count : getCount,
loadCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load(['DashboardCtrl.js']);
}]
}
}).
The page loads in this case, but the profileData and count doesn't get injected into the controller. The controller definition is as given below.
var app = angular.module('gt');
app.controller('DashboardCtrl', ['$scope', '$rootScope', 'profileData', 'count',
function($scope, $rootScope, profileData, count) {
...
}]);
On debugging, I realise that the getProfile and getCount method get's called, but it happens asynchronously and the controller also lazy loads without waiting for these methods. How do I inject and lazy load at the same time? Can I use promises to resolve this in any way?
I am using AngularJS 1.3.10 & ocLazyLoad 1.0.5 versions
getProfile function for reference
var getProfile = function($q, $http, Profile, localStorageService) {
var deferred = $q.defer();
if (!localStorageService.get("loggedInUser")) {
$http.post('/loggedin').success(function(user) {
if (user) {
localStorageService.set("loggedInUser", user.email)
Profile.get(localStorageService.get("loggedInUser"), function(profileData) {
if (profileData) {
deferred.resolve(profileData);
} else {
deferred.resolve();
}
});
} else {
deferred.reject();
}
});
} else {
Profile.get(localStorageService.get("loggedInUser"), function(profileData) {
if (profileData) {
deferred.resolve(profileData);
} else {
deferred.resolve();
}
});
}
return deferred.promise;
}
getProfile.$inject = ["$q", "$http", "Profile", "localStorageService"];
I could get this working with the following configuration of $routeProvider
when("/dashboard", {
templateUrl: "dashboard.html",
controller :"DashboardCtrl"
resolve: {
profileData: getProfile,
count : getCount,
loadCtrl: ['$ocLazyLoad', function($ocLazyLoad) {
return $ocLazyLoad.load(['DashboardCtrl.js']);
}]
}
}).
where DashboardCtrl is the controller defined in DashboardCtrl.js
does getProfile and getCount return a promise? I would guess this is the problem as this is required. Every object put in resolve should return a promise. see the documention
If you need your resolves to happen in a specific order, you can inject them into another resolve, like this:
when("/dashboard", {
templateUrl: "dashboard.html",
resolve: {
profileData: getProfile,
count : getCount,
loadCtrl: ['$ocLazyLoad', 'profileData', 'count', function($ocLazyLoad, profileData, count) {
return $ocLazyLoad.load(['DashboardCtrl.js']);
}]
}
}).

how to call angular factory method from module setup?

I have a module App and factory i18n, what is the best way to call i18n.load
method form App (config? run? etc?)
angular
.module('App', [
'ngRoute',
'service.i18ndb'
])
.config(function ($routeProvider) {
//want to i18n.load() here somehow
$routeProvider
.when('/signin', {
templateUrl: '../views/sign-in.html',
controller: 'SigninCtrl'
})
.when('/mix', {
templateUrl: '../views/mix.html',
controller: 'MixCreateCtrl'
})
.otherwise({
redirectTo: '/signin'
});
});
angular.module('App')
.factory('service.i18ndb', function() {
return {
load: function() { console.log("Busy"); }
}
}
);
The problem you will always have if you use .run is having to deal with a page that has no i18n loaded. This means you will need to have a way to deal with your view when their is no i18n loaded. You can either hide it or the text will flash with the wrong values at first.
However, AngularJS gives you a wonderful feature to make sure it is loaded before your view is loaded: the resolver!
Here is how to do it.
var i18nResolver = function(service.i18ndb) {
return service.i18ndb.promise;
};
$routeProvider
.when('/signin' {
templateUrl: '../views/sign-in.html',
controller: 'SigninCtrl',
resolve: {
i18n: i18nResolver
}
});
You can fix this code to use the correct promise of your HTTP request or whatever service you are using.
One of the benefits of using this way is you can have a different labels for a different page for your i18n and use the i18n service to recover them no matter where you are.
You are defining your app module twice. One you create your factory, it can be injected to the controller and used there. You could try something like this:
angular.module('App', ['ngRoute','service.i18ndb'])
.factory('service.i18ndb', function() {
return {
load: function() { console.log("Busy"); }
}
})
.config(function ($routeProvider) {
//want to i18n.load() here somehow
$routeProvider
.when('/signin', {
templateUrl: '../views/sign-in.html',
controller: 'SigninCtrl'
})
.when('/mix', {
templateUrl: '../views/mix.html',
controller: 'MixCreateCtrl'
})
.otherwise({
redirectTo: '/signin'
});
})
.controller('SigninCtrl', function($scope, service.i18ndb) {
// Call your factory function here
service.i18ndb.load();
// If the function returns a value you could assign it to a scope
// variable so it can be used in your template 'sign-in.html'
$scope.your_variable = service.i18ndb.load();
});
angular
.module('App', [
'ngRoute'
])
.config(function ($routeProvider) {
//want to i18n.load() here somehow
$routeProvider
.when('/signin', {
templateUrl: '../views/sign-in.html',
controller: 'SigninCtrl'
})
.when('/mix', {
templateUrl: '../views/mix.html',
controller: 'MixCreateCtrl'
})
.otherwise({
redirectTo: '/signin'
});
})
.run(['i18ndb', function(i18ndb) {
i18ndb.load();
}])
.factory('i18ndb', function() {
return {
load : function() {console.log('test')}
};
});
);
You were requiring a module which has not been defined (as far as I can tell). The factory you were adding was on the 'App' module not the 'service.i18ndb'.
You then need to dependency inject the i18ndb factory in to the run method to call it from there (presuming that you want to call that function to bootstrap your app).

$routeParams Empty on $locationChangeSuccess

I configure my app in the following run block. Basically I want to preform an action that requires me to know the $routeParams every $locationChangeSuccess.
However $routeParams is empty at this point! Are there any work rounds? What's going on?
app.run(['$routeParams', function ($routeParams) {
$rootScope.$on("$locationChangeSuccess", function () {
console.log($routeParams);
});
}]);
UPDATE
function configureApp(app, user) {
app.config(['$routeProvider', function ($routeProvider) {
$routeProvider.
when('/rentroll', {
templateUrl: 'rent-roll/rent-roll.html',
controller: 'pwRentRollCtrl'
}).
when('/bill', {
templateUrl: 'bill/bill/bill.html',
controller: 'pwBillCtrl'
}).
when('/fileroom', {
templateUrl: 'file-room/file-room/file-room.html',
controller: 'pwFileRoomCtrl'
}).
when('/estate-creator', {
templateUrl: 'estate/creator.html'
}).
when('/estate-manager', {
templateUrl: 'estate/manager.html',
controller: 'pwEstateManagerCtrl'
}).
when('/welcomepage', {
templateURL: 'welcome-page/welcome-page.html',
controller: 'welcomePageCtrl'
}).
otherwise({
redirectTo: '/welcomepage'
});
}]);
app.run(['$rootScope', '$routeParams', 'pwCurrentEstate','pwToolbar', function ($rootScope, $routeParams, pwCurrentEstate, pwToolbar) {
$rootScope.user = user;
$rootScope.$on("$locationChangeSuccess", function () {
pwToolbar.reset();
console.log($routeParams);
});
}]);
}
Accessing URL:
http://localhost:8080/landlord/#/rentroll?landlord-account-id=ahlwcm9wZXJ0eS1tYW5hZ2VtZW50LXN1aXRlchwLEg9MYW5kbG9yZEFjY291bnQYgICAgICAgAoM&billing-month=2014-06
this worked for me, inside your run callback:
.run(function($rootScope, $routeParams, $location) {
$rootScope.$on("$locationChangeSuccess", function() {
var params = $location.search();
console.log(params);
console.log('landlord-account-id:', params['landlord-account-id']);
console.log('billing-month', params['billing-month']);
});
})
By accessing /rentroll you will get empty $routeParams because your $routeProvider
configuration for that path is not expecting any variable on URL.
$routeParams is used for getting variable value of your URL. For example :
$routeProvider
.when('/rentroll/:variable', {...});
and access it on /rentroll/something will give you $routeParams as below :
$routeParams.variable == 'something';
https://docs.angularjs.org/api/ngRoute/service/$routeParams
Explains a bit about when the $routeParams are available and why.
To solution here might be to use $route.current.params

How can you manually inject route resolve data inside a controller?

I have 2 routes that share a controller, one needs data resolved prior to the view loading, and the other does not need the resolved data.
Routing segment example:
...
when('/users', {
controller: 'UsersCtrl',
templateUrl: '/partials/users/view.html',
resolve: {
resolvedData : ['Accounts', function(Accounts) {
return Accounts.get();
}]
}
}).
when('/users/add', {
controller: 'UsersCtrl',
templateUrl: '/partials/users/add.html'
})
...
Controller example:
app.controller('UsersCtrl', ['$scope', 'Helper', 'resolvedData',
function($scope, Helper, resolvedData) {
// this works for the first route, but fails for the second route with
// unknown "resolvedDataProvider"
console.log(resolvedData);
}]);
Is there any way I can get the resolvedData in the controller without explicitly using the resolve name as a dependency? So a check can be performed?
Using the $injector does not work. I would like to do something similar to:
if ($injector.has('resolveData')) {
var resolveData = $injector.get('resolveData');
}
However this does not work even for the route that has the resolveData set ('/users'):
app.controller('UsersCtrl', ['$scope', 'Helper', '$injector',
function($scope, Helper, $injector) {
// this does not work -> fails with the unknown "resolvedDataProvider" as well
$injector.get('resolvedData');
}]);
Can this be done in angularjs? Or should I just create a new controller?
Thank you.
Looks like I figured out another way to go. The resolved data is part of the $route. So you can access it using:
app.controller('UsersCtrl', ['$scope', '$route', 'Helper',
function($scope, $route, Helper) {
if ($route.current.locals.resolvedData) {
var resolvedData = $route.current.locals.resolvedData;
}
}]);
If the other route doesn't need it, just inject undefined on that route:
router:
when('/users', {
controller: 'UsersCtrl',
templateUrl: '/partials/users/view.html',
resolve: {
resolvedData : ['Accounts', function(Accounts) {
return Accounts.get();
}]
}
}).
when('/users/add', {
controller: 'UsersCtrl',
templateUrl: '/partials/users/add.html',
resolve: {
resolvedData: function() {
return undefined;
}
}
})
controller:
app.controller('UsersCtrl', ['$scope', 'Helper', 'resolvedData',
function($scope, Helper, resolvedData) {
if(resolvedData){
//set some scope stuff for it
} else {
//do what you do when there is no resolvedData
}
}]);

Categories