Angular, delay of initial load - javascript

I am using a generator here - https://github.com/DaftMonk/generator-angular-fullstack, and I am having a small problem when it initially loads.
There is a second or 2 when it loads when you can see the index.html template without partial css on it, and then it loads the user to the login page. I would like to get rid of this, so when you go to the root, you would go straight to /login. I'm not sure where to begin with this, as it could be many things. I should not I have alot of bower'd in scripts, maybe they are slowing this down?
My first thought was it was the authentication, but maybe it has nothing to do with it. But here is the app js minus the dependancies
.config(function ($routeProvider, $locationProvider, $httpProvider) {
$routeProvider
.otherwise({
redirectTo: '/'
});
$locationProvider.html5Mode(true);
$httpProvider.interceptors.push('authInterceptor');
})
.factory('authInterceptor', function ($rootScope, $q, $cookieStore, $location) {
return {
// Add authorization token to headers
request: function (config) {
config.headers = config.headers || {};
if ($cookieStore.get('token')) {
config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
}
return config;
},
// Intercept 401s and redirect you to login
responseError: function(response) {
if(response.status === 401) {
$location.path('/login');
// remove any stale tokens
$cookieStore.remove('token');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
};
})
.run(function ($rootScope, $location, Auth) {
// Redirect to login if route requires auth and you're not logged in
$rootScope.$on('$routeChangeStart', function (event, next) {
Auth.isLoggedInAsync(function(loggedIn) {
if (next.authenticate && !loggedIn) {
$location.path('/login');
}
});
});
});
It could entirely not be this, but I think it might be. I don't care if the page is blank for second, but it's showing the items in the top like the header menu that is in the base index.html with a little css styling on it, but it looks terrible. Any direction in where to look to fix this would be MUCH appreciated, as I have no idea where to even start.
Thanks for reading!

Related

how to restrict view in angularjs application

I want to restrict user's to access /dashboard view and /add-item view or any other view in my angular js application.
this is my router
app.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise('login');
$stateProvider.
state('app.dashboard', {
url: '/dashboard',
templateUrl: appHelper.viewsPath('dashboard/views/dashboard'),
controller: 'DashBoardController as DashBordCtrl',
}).
// Add Item
state('app.add-item', {
url: '/add-item',
templateUrl: appHelper.viewsPath('item/views/add-item'),
controller: 'ItemController as ItemCtrl',
})
});
After login I am storing the token in my local storage. I want user to not access any view if token is not present.
this is my login controller
$scope.register = function(credentials){
LoginService.post(credentials,function(success){
$state.go('app.add-item');
SessionService.set("token",success.accessToken);
},function(error){
FlashService.showError("Please Enter Valid Email Password");
});
}
}
On 401 error i am redirecting to login page like this:
app.config(function ($httpProvider) {
// $http.defaults.headers.common.Authorization = '';
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.interceptors.push(function ($location, $q, SessionService, FlashService) {
return {
request: function (config) {
config.headers = config.headers || {};
config.headers.Authorization = SessionService.get('token');
return config;
},
responseError: function (response) {
if (response.status === 401) {
SessionService.unset('token');
$location.path('/login');
}
return $q.reject(response);
}
};
});
});
But if I type /add-item in url, my add-item page is opening and then it suddenly close,because server return 401 ,and login page appear.I don't want to open any view if user is not login.
I am new in angularjs and i am confusing how to do this. Please help.
If token is not present,You can save the user's location to take him back to the same page after he has logged-in.You can review following code in app.js:
app.config(function($stateProvider, $urlRouterProvider){
$stateProvider.
state('app.dashboard', {
url: '/dashboard',
templateUrl: appHelper.viewsPath('dashboard/views/dashboard'),
controller: 'DashBoardController as DashBordCtrl',
}).
// Add Item
state('app.add-item', {
url: '/add-item',
templateUrl: appHelper.viewsPath('item/views/add-item'),
controller: 'ItemController as ItemCtrl',
})
$urlRouterProvider.otherwise('login');
});
$urlRouterProvider.otherwise('login') replace with $urlRouterProvider.otherwise('app/dashboard') in app.config.if it is $urlRouterProvider.otherwise('login') ,you have a token ,throw login page still.
app.config(function ($httpProvider) {
// $http.defaults.headers.common.Authorization = '';
delete $httpProvider.defaults.headers.common['X-Requested-With'];
$httpProvider.interceptors.push(function ($location, $q, SessionService, FlashService) {
return {
request: function (config) {
config.headers = config.headers || {};
config.headers.Authorization = SessionService.get('token');
return config;
},
responseError: function (response) {
if (response.status === 401) {
SessionService.unset('token');
$location.path('/login');
}
return $q.reject(response);
}
if (!SessionService.get('token');) {
/* You can save the user's location to take him back to the same page after he has logged-in */
$rootScope.savedLocation = $location.url();
$location.path('/login');
}
};
});
});
You should use resolve in your route configuration. Example:
state('app.add-item', {
url: '/add-item',
templateUrl: appHelper.viewsPath('item/views/add-item'),
controller: 'ItemController as ItemCtrl',
resolve: ['SomeService', function(SomeService) {
return SomeService.getDataFromApi();
}]
})
Looking at this example, if SomeService returns 401, meaning an error, that route controller will never instantiate and therefor you cannot access the view. Your interceptor will still do his job, and redirect to login when 401 happens, but now it will happen smoothly and with no flash of the view.
This is a good way to do, and it will solve that problem of yours and also some others you might encounter later.

AngularJS: Retain Login information when page refresh

I am trying to retain the user information when page refresh. I used cookieStore for this purpose. So my run module in the Angular App looks like this.
.run(['$rootScope', '$cookieStore', '$state', function($rootScope, $cookieStore, $state) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, $location){
var requireLogin = toState.data.requireLogin;
if(typeof $rootScope.user == 'undefined'){
$rootScope.user=$cookieStore.get("user");
$rootScope.sessionid=$cookieStore.get("session");
}
if(requireLogin && typeof $rootScope.user === 'undefined'){
event.preventDefault();
$state.go('login', null, {notify: false}).then(function(state) {
$rootScope.$broadcast('$stateChangeSuccess', state, null);
});
}
});
Two main things I wanted to achieve from this is,
Have to get the user and sessioninfo from the browser's local storage, when page refresh.
If user is undefined, then it has to be redirected to the login page. It is for restricting the users to go to intermediate pages without login.
If user is undefined, and data is not available in the local storage, the first if statement gives error and the second if statement doesnot work.
So when the user tries to visit any page the first time without going to login page, it is not redirecting to the login page, because the code failed in first if statement, the second if not working.
How can I achieve both the functionalities together?
Thank you
You can create an authInterceptor factory and push it in interceptors.
This method will always check if user is logged in or not on each page and will throw user on login page if he is not authenticated
For purposes of global error handling, authentication, or any kind of
synchronous or asynchronous pre-processing of request or
postprocessing of responses, it is desirable to be able to intercept
requests before they are handed to the server and responses before
they are handed over to the application code that initiated these
requests. The interceptors leverage the promise APIs to fulfill this need for >both synchronous and asynchronous pre-processing.
Learn more about Interceptors
'use strict';
angular.module('app', [
'ngCookies',
'ngResource',
'ngSanitize',
'ngRoute'
])
.config(function($routeProvider, $locationProvider, $httpProvider) {
$routeProvider
.otherwise({
redirectTo: '/'
});
$httpProvider.interceptors.push('authInterceptor');
})
.factory('authInterceptor', function($rootScope, $q, $cookieStore, $location) {
return {
// Add authorization token to headers
request: function(config) {
config.headers = config.headers || {};
if ($cookieStore.get('token')) {
config.headers.Authorization = 'Bearer ' + $cookieStore.get('token');
}
return config;
},
// Intercept 401s and redirect you to login
responseError: function(response) {
if (response.status === 401) {
$location.path('/login');
// remove any stale tokens
$cookieStore.remove('token');
return $q.reject(response);
} else {
return $q.reject(response);
}
}
};
})
.run(function($rootScope, $location, Auth) {
// Redirect to login if route requires auth and you're not logged in
$rootScope.$on('$routeChangeStart', function(event, next) {
Auth.isLoggedInAsync(function(loggedIn) {
if (next.authenticate && !loggedIn) {
$location.path('/login');
}
});
});
})
.factory('Auth', function Auth($location, $rootScope, $http, User, $cookieStore, $q) {
var currentUser = {};
if ($cookieStore.get('token')) {
currentUser = User.get();
}
return {
/**
* Gets all available info on authenticated user
*
* #return {Object} user
*/
getCurrentUser: function() {
return currentUser;
},
/**
* Check if a user is logged in
*
* #return {Boolean}
*/
isLoggedIn: function() {
return currentUser.hasOwnProperty('role');
},
/**
* Waits for currentUser to resolve before checking if user is logged in
*/
isLoggedInAsync: function(cb) {
if (currentUser.hasOwnProperty('$promise')) {
currentUser.$promise.then(function() {
cb(true);
}).catch(function() {
cb(false);
});
} else if (currentUser.hasOwnProperty('role')) {
cb(true);
} else {
cb(false);
}
}
};
})
.factory('User', function($resource) {
return $resource('/api/users/:id/:controller', {
id: '#_id'
}
});
});
With Above Mechanism, you can use Auth service to get user info in any controller or directives as:
.controller('MainCtrl', function ($scope, Auth) {
$scope.currentUser = Auth.getCurrentUser;
});
in template file:
<div ng-controller="MainCtrl">
<p> Hi {{currentUser().name}}!</p>
</div>
Note: You need to create a proper REST API in order to get correct user data
I would look into using ngStorage. I use the sessionStorage object which will retain the data even on refresh. If you need further implentation example please let me know but the documentation is great.
https://github.com/gsklee/ngStorage

AngularJS - disable some routes

As title already suggests, I'm trying to disable some routes. I'm using angular seed project, that already has a nice structure.
I'm using JWT and I'm trying to set up a structure where if a certain route requires user to be logged in, and the user is not logged in, it redirects him to the some other page.
On my angular.module I've added the following code:
.run(['$rootScope', 'userService', '$location', function($rootScope, userService, $location) {
userService.init();
$rootScope.$on('$routeChangeStart', function(event, next, current) {
$rootScope.isPrivate = next['authenticate'];
if ($rootScope.isPrivate) {
if (!userService.get())
$location.path('/');
}
});
}]);
And this is a protected route:
angular.module('myApp.view2', ['ngRoute', 'ngCookies'])
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/admin/vnos-stevilke', {
templateUrl: 'view2/view2.html',
controller: 'View2Ctrl',
authenticate: true
}).when('/admin/vnos-stevilke/:id', {
templateUrl: 'view2/view2.html',
controller: 'View2Ctrl',
authenticate: true
});
}])
.controller('View2Ctrl', ['$scope', 'webServices', '$location', '$routeParams', function($scope, webServices, $location, $routeParams) {
if ($routeParams.id)
webServices.getBranchById($routeParams.id, function(err, data) {
$scope.branch = data;
});
webServices.getCompanies(function(err, data) {
console.log(data);
console.log('no access!');
if (!err)
$scope.companies = data;
});
}]);
now at first it appears to be working OK: if I'm not logged in, the route is not displayed and I get redirected back to the root. But at a closer look I've noticed that console.log('no access!'); is still displayed in the console. So it appears that controller gets initialized.
It seems like the whole route is loaded and then gets redirected if user is not logged in. That is not the behaviour I'm looking for. I'm trying to HOLD the loading of the route until I'm sure the user is logged in.
Any ideas?
UPDATE:
I changed the code accordingly with the suggestion below, but it doesn't seem to work. Where have I gone wrong?
userService method that checks if user is logged in:
this.isLogged = function() {
var deferred = $q.defer();
if (current === null) return deferred.reject();
else return deferred.resolve(current);
};
Run method:
.run(['$rootScope', 'userService', '$location', function($rootScope, userService, $location) {
userService.init();
$rootScope.$on('$routeChangeError', function() {
$location.path('/');
});
}]);
Restricted page:
.config(['$routeProvider', function($routeProvider) {
$routeProvider.when('/admin/vnos-stevilke', {
templateUrl: 'view2/view2.html',
controller: 'View2Ctrl',
resolve: function(userService) {
console.log('test');
return userService.isLogged();
}
});
}])
Here, the "test" never displays in console.
You need to just decide if you are letting user into restricted area with resolve route parameter.
If in one of resolve function resolves with a promise object that is rejected it stops entering requested route.
I would write something like:
$routeProvider.when('/restrictedURL',{
...some params,
resolve: function(userService){
return userService.get();
}
}
...and make userService.get return a Promise object that is resolved if session is active and rejected otherwise.
Now.. if promise is rejected a route won't be launched and $routeChangeError event is raised, so you need something like:
angular.module('yourapp').run(function($rootScope){
$rootScope.$on("$routeChangeError",function(){
$location.path('/');
});
});
read more about resolve parameter # https://docs.angularjs.org/api/ngRoute/provider/$routeProvider

Interceptors for managing restricted pages in AngularJS

I just started a few days ago with AngularJS and I'm having issues with my interceptor that intercepts 401 statuses from server responses.
It broadcasts a message of the type "loginRequired" when a 401 is returned and a redirect is triggered on that event.
The issue is that if I try to access a restricted page while not being logged in, I can see the page flash for a moment before I'm redirected to the login page. I'm still fairly a beginner in asynchronous stuff, promises etc. Could somebody point out what I'm doing wrong?
Here's my interceptor. As you can see it's really simple but I slimmed it down to explain my point and I'm trying to understand things before developing it further.
The interceptor
var services = angular.module('services', []);
services.factory('myInterceptor', ['$q', '$rootScope',
function($q,$rootScope) {
var myInterceptor = {
'responseError': function(rejection) {
$rootScope.$broadcast('event:loginRequired');
return $q.reject(rejection);
}
};
return myInterceptor;
}
]);
The injection of my interceptor
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
}]);
The route for the restricted page
.when('/restrictedPage', {
templateUrl: 'partials/restrictedPage.html',
controller: 'RestrictedPageController'
}).
The restricted page controller
controllers.controller('RestrictedPageController', function($scope) {
//Some times the alert pops up, sometimes not.
alert("Damn it I shouldn't be there");
});
The $rootScope event watcher
$rootScope.$on('event:loginRequired', function() {
//Only redirect if we aren't on free access page
if ($location.path() == "/freeAccess")
return;
//else go to the login page
$location.path('/home').replace();
});
My issue is clearly with the way I handle the interceptor and $q. I found another way of creating the interceptor on github but it's not the way the official documentation uses, so I think it might be the old way and it's not as clean as putting it in a factory in my opinion. He just puts this code after defining the routes in the config function of his module. But this code works and I don't get the page flash.
Another way I found on Github
var interceptor = ['$rootScope', '$q', '$log',
function(scope, $q, $log) {
function success(response) {
return response;
}
function error(response) {
var status = response.status;
if (status == 401) {
var deferred = $q.defer();
var req = {
config: response.config,
deferred: deferred
};
scope.$broadcast('event:loginRequired');
return deferred.promise;
}
// otherwise
return $q.reject(response);
}
return function(promise) {
return promise.then(success, error);
};
}
];
$httpProvider.responseInterceptors.push(interceptor);
But my goal is not just to "make it work" and I hate the mantra "If it's not broken don't fix it". I want to understand what's the issue with my code. Thanks!
Instead of broadcasting 'event:loginRequired' from your interceptor, try performing the location path change within your interceptor. The broadcast would be increasing the delay between receiving the 401 and changing the location and may be the cause of the screen 'flash'.
services.factory('myInterceptor', ['$q', '$rootScope', '$location',
function($q, $rootScope, $location) {
var myInterceptor = {
'responseError': function(rejection) {
if (response.status === 401 && $location.path() !== '/freeAccess') {
//else go to the login page
$location.path('/home').replace();
}
// otherwise
return $q.reject(response);
}
};
return myInterceptor;
}
]);
You could also perform a HTTP request when your app module first runs to determine right away if the user is authorised:
myApp.config(['$httpProvider', function($httpProvider) {
$httpProvider.interceptors.push('myInterceptor');
}])
.run(function($http) {
//if this returns 401, your interceptor will be triggered
$http.get('some-endpoint-to-determine-auth');
});

AngularJS Routing with Authorization Process

I'm trying to implement basic authentication routing in AngularJS. I have a model that has a authorize method that returns a promise. I want the routing to wait until that authorize function has returned true or false to continue, once that has completed it should resume the path or redirect the user to the login page.
I think essentially i need to stop routing, call that method and then resume or redirect to login. Below is the code I have so far but i'm not sure how to accomplish the pause/resume. Any ideas?
return angular.module('d', ['ngCookies', 'ngRoute'])
.config(['$routeProvider', '$locationProvider', '$httpProvider',
function ($routeProvider, $locationProvider, $httpProvider) {
$routeProvider.when('/',
{
templateUrl: 'views/home.html',
controller: 'HomeCtrl'
});
$routeProvider.when('/login',
{
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
});
$routeProvider.otherwise({ redirectTo: '/404' });
$locationProvider.html5Mode(true);
// handle unauthorized requests by redirecting to login page
$httpProvider.responseInterceptors.push(
['$location', '$q', function ($location, $q) {
function success(response) {
return response;
}
function error(response) {
if (response.status === 401) {
$location.path('/login');
return $q.reject(response);
}
else {
return $q.reject(response);
}
}
return function (promise) {
return promise.then(success, error);
}
}]);
}])
.run(['$rootScope', '$location', 'Auth', function ($rootScope, $location, Auth) {
$rootScope.$on("$routeChangeStart", function (event, next, current) {
$rootScope.error = null;
Auth.authorize().then(function(){
$location.path('/');
},function(){
$location.path('/login');
});
});
}]);
Your solution is very similar to a prototype I wrote a while back.
The idea is that whenever you "touch" the server and get an authentication error, a modal window pops-up asking for a login without changing the URL (you let it change to the new URL and stay there).
My implementation was also based on an interceptor checking for 401. It has a dependency on $rootScope and sets a property "needsLogin" to true. The page template has the login modal window visible when needsLogin === true and hides the ng-view (this is important since the new route has been loaded but it misses its data). Finally, upon successfull login, the login controller does the $route.reload() and then sets $rootScope.needsLogin = false.
Small snippets:
<div id="main" role="main" ng-show="needsLogin === false">
<div ng-view></div>
</div>
<div ng-show="needsLogin === true" ng-include="'views/login.html'" class="overlay"></div>
The login controller can be something like:
function LoginCtrl($scope, $rootScope, $route, $http) {
$scope.login = function () {
$http.post( /* Somehow do your login */ )
.success(function () {
var deregister = $rootScope.$on('$routeChangeSuccess', function () {
// hide login / show ng-view after route has been reloaded
$rootScope.needsLogin = false;
deregister();
});
$route.reload();
})
.error(function () {
/* handle errors */
});
};
}
$route.reload() is not a full page refresh, it merely re-initializes the route (controller/view etc). Hopefully, the call that was rejected before will run again and the page will be fine.

Categories