In my AngularJS application I want to catch errors and send the user to a 404 state like so:
$rootScope.$on('$stateNotFound', function(event, unfoundState, fromState, fromParams){
//console.log(unfoundState.to);
//console.log(unfoundState.toParams);
//console.log(unfoundState.options);
$state.go('404');
});
This is for when a user clicks a bad link (e.g. has no state, as ones with states but no content are handled by the otherwise method) in the app, etc.
The code loads the 404 state fine:
.state('404',
{
views: {
'body': {
templateUrl: 'partials/404.html',
}
}
});
But what I want to do is change the URL to the state they tried to access, so basically load the 404 state but use the incorrect URL (as a 404 would work in a server environment).
I've looked into doing:
history.replaceState(null, null, unfoundState.to);
$state.go('404');
But that causes major errors in the app and changes the URL but not the state!
How can I do this?
Try this to catch all URLs not matching any of your previous routes :
.state("404", {
url: "*path", // will catch all URLs
templateUrl: 'partials/404.html'
})
According to the docs, I think you can do:
$state.go('404',{},{location:false});
Note: I'm in the process of creating a plunkr to verify but I don't have time to finish it up completely right now
Related
I have a angularJS application which is using angular ui-router for routing.
I added the following level of security but that doesn't helped me out:
I am storing token and user role in local storage.
on the basis of user roles i'm redirecting user to routes.
I have applied watch service on local storage but on refresh i
didn't get last(old) value of local storage so in this case my check(watch) fails.
when someone changes token at local storage and refresh site.
then i'm not able to check whether token is changed or not.
In this case user is able to view template but not able to fetch data cause i have added security layer at the backend. but i need to prevent access to template also.
Please help me add security at angular (client) to overcome these issues.
If i understand correctly, you are looking for a way to prevent the user from seeing the template if they do not have the required permissions. What i did is, when changing pages, first check to see if the user has the correct permission. In each state, i add the permission required to get to the page.
//these are the permissions the user currently has
var currentPermissions = {
"User.Read":true,
"User.Create":false
}
var app = angular.module('myApp', ['ui.router']);
app.config( function($stateProvider) {
$stateProvider.state('page1', {
url: "/page1",
data: {permission: "User.Read"}, //the perm required
template: "Page 1"
})
.state('page2', {
url: "/page2",
data: {permission: "User.Read"},
template: "Page 2"
})
.state('page3', {
url: "/page3",
data: {permission: "User.Create"},
template: "Page 3"
})
.state("noAccess", {
url:"/noAccess",
template: "access denied"
});
});
app.run(function($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
//check to make sure user has correct perm
if(toState.data && !currentPermissions[toState.data.permission]){
event.preventDefault();
$state.go('noAccess');//redirect if user does not have perm
}
});
});
Fiddle at: https://jsfiddle.net/Darin_Cardin/mwg33rm4/
I am using AngularJS and ui-router and have the following code which catches 404's and loads in a 404 template:
$urlRouterProvider.otherwise(function($injector, $location){
$injector.invoke(['$state', function($state) {
$state.go('404');
}]);
});
It keeps the URL intact instead of redirecting, but shows the 404 template:
.state('404',
{
views: {
'body': {
templateUrl: 'partials/404.html',
}
}
});
Normally it would redirect to the root state with: $urlRouterProvider.otherwise('/');
However when HTML5 mode is turned off and the user visits the root URL e.g. http://www.domain.com/app/ it loads the 404 state instead of redirecting to http://www.domain.com/app/#/
How can I keep the 404 state code above, but have it redirect to the hashbang when accessing the initial page? So effectively only have the 404 load if the request is anything other than the home page?
I can't use the $stateNotFound or $stateChangeSuccess and need a way to check if it's a home page request within the actual config setup itself that can toggle between the / and the state.404 within the otherwise statement.
So something along the lines of (which does seem to work).
$urlRouterProvider.otherwise(function($injector, $location){
$injector.invoke(['$state', function($state) {
if( $location.$$path == '')
$state.go('home'); // redirect to /#/
else
$state.go('404'); // else go to 404 state
}]);
});
But is there a better way to handle this?
Use a httpErrorResponseInterceptor! This will get fired when a httpResponse happens. Here is an example
angular
.module('myApp')
.factory('httpErrorResponseInterceptor', ['$q', '$state', function ($q, $state) {
return {
responseError: function error(response) {
switch (response.status) {
case 404:
$state.go('404');
break;
default:
console.log("Unhandled HTTP error:", response);
}
return $q.reject(response);
}
};
}
]);
I create an app with requirement of template files being in two folders - first is available without restrictions and second is available only for logged in users.
Things are complicating when somebody refresh the page and session expire before. Angular throws error (e.g. 401) in console while loading template file which I want to avoid. Is there a way to catch that event and access response status to for e.g. redirect the page?
For your case, we can catch the error and redirect to a page with the help of $location.url(). Here is the concept. Now i have a controller and in that, i have success function and error function. Whenever we get an error, the error function will run and we can pass the link of the page you want to redirect.
$http.get(url,[params])
.success(function(data, status, headers, config){
// bind your data to scope
})
.error(function(data, status, headers, config) {
$location.url('/404');
});
By the use of $routeProvider, you can configure the function something like
this :
$routeProvider
.when('/404', {
templateUrl: '404.html',
controller: 'yourcontroller'
});
And you can see the description for $location.url() here
Hope it works
I'm developing an application using Laravel and AngularJS. For AngularJS pretty URL , i have set $locationProvider.html5Mode(true) and it's working fine. But suppose my url is http://localhost:8000/demo and if i refresh the page, I'm getting NotFoundHttpException in compiled.php line 7693:
Here is my angular's routes.
function($routeProvider,$locationProvider) {
$locationProvider.html5Mode(true);
$routeProvider.
when('/', {
templateUrl: 'partials/index.html',
controller: 'indexController'
}).
when('/dom',{
templateUrl:'partials/dom.html',
controller:'DomController'
}).
when('/demo',{
templateUrl:'partials/demo.html',
controller:'DemoController'
});
}]);
And here's my laravel's route.
Route::get('/', function(){
return view('index');
});
I'd appreciate a little help.
Thanks!
The problem here is that the web server will pick up http://localhost:8000/demo when you refresh the page, and try to handle the request. It's not aware that you wish to use html5 routing. So it will parse the request, say "oh, I should pass this to public/index.php and be done with it". And then public/index.php will receive it and throw an error since the route doesn't exist in Laravel.
What you need to do is to make a catch all type of route in Laravel, and then let Angular's routing take over. You then render your index view on every single request. There's a great answer here on SO on how you do that in Laravel 5: How do I catch exceptions / missing pages in Laravel 5?
This is pseudo code and will not work, just to show an example:
Route::get('*', function() {
return view('index');
});
So, render the index view on every request and then let Angular handle the routing from there.
I'm trying to sort out a login system using cookies so that the user's login will persist after leaving the app. I am able to set the cookie correctly, but I am unclear as to how I can use the stored cookie to limit the users access to the login screen if they are already logged in.
I think the best way to do this would be within the routes. This is what my file currently looks like:
var routes = angular.module('we365', ['rcForm', 'ngCookie', 'ngCookies']);
routes.config(function ($routeProvider) {
$routeProvider
.when('/login', {
templateUrl: 'views/login.html',
controller: 'loginCtrl'
})
.when('/', {// get digest view
templateUrl: 'views/getDigest.html',
controller: 'GetDigestCtrl'
})
.when('/artifact/:artifact_id', {// single artifact view
templateUrl: 'views/artifact.html',
controller: 'artifactCtrl'
})
.otherwise({
redirectTo: '/'
});
});
Also, i'd like to hide the 'login' button from the parent view so that the user isn't able to click on it. This is what the view looks like now:
<div class="container">
<div class="page-header col col-lg-12">
<h1>Welcome!</h1>
Login
Load Digest Data
</div>
</div>
There are many ways, I have two that are my favorite:
1) Check on route change
angular.module('MyApp', [])
.run(function($rootScope, myLoginService) {
$rootScope.$on('$routeChangeStart', function () {
if (!myLoginService.isUserLoggedIn()) {
$location.path('/login');
}
});
You can replace the isUserLogged for a mapper service that receives where the user wants to go; if the user has the proper privileges (either stored within a cookie or local storage in the form of a token), then let the route succeed. Otherwise, show an error, or route him to wherever you want. In my case, the myLoginService checks for a localStorage.
2) Any data request to a server has a token included to headers; failed requests (401) are intercepted and stored, while the user is redirected
This one is more for CRUD apps and not necessarily for routing, but the concept is simple: a user A can perform N actions as long as he/she has the privileges to do so; if he tries to perform an action (or M actions) that he's not allowed, then the request is intercepted and queued in order to ask him to authenticate with a user that CAN do those actions
.factory('securityInterceptor', ['$injector', 'securityRetryQueue', function($injector, queue) {
return function(promise) {
// Intercept failed requests
return promise.then(null, function(originalResponse) {
if(originalResponse.status === 401) {
// The request bounced because it was not authorized - add a new request to the retry queue
promise = queue.pushRetryFn('unauthorized-server', function retryRequest() {
// We must use $injector to get the $http service to prevent circular dependency
return $injector.get('$http')(originalResponse.config);
});
}
return promise;
});
};
}]);
Again, this is for more "data like" requests and not necessarily for routing. This was stolen from the AngularJS sample app. You should check it for more examples.