I have a question: When I'm in a page I want return to previous page. I use the $routeProvider. How can I read the previous url?
I try to use this code in my controller but doesn't work...
angular.module.controller('myController',
function($scope, $routeParams, $http, $location, $rootScope) {
$rootScope.$on("$routeChangeSuccess",
function (event, current, previous, rejection) {
console.log(previous);
$location.path('PREVIOUS PATH');
});
});
How can I read the previous path? Thanks!
I am not fully sure, what you want to achieve. So I would suggest, check this before you go your own way:
How to implement history.back() in angular.js
But, in case, you want to know how to keep the last state with angular and UI-Router, we can do it with a service. There is some naive implementation tracking just last state (not challenging the history.back())
Check the working example
Service definition:
.factory('PreviousState', ['$rootScope', '$state',
function ($rootScope, $state) {
var lastHref = "/home",
lastStateName = "home",
lastParams = {};
$rootScope.$on("$stateChangeSuccess", function (event, toState, toParams
, fromState, fromParams) {
lastStateName = fromState.name;
lastParams = fromParams;
lastHref = $state.href(lastStateName, lastParams)
})
return {
getLastHref: function (){ return lastHref ; },
goToLastState: function (){ return $state.go(lastStateName, lastParams); },
}
}])
So we just do listen the $stateChangeSuccess and keep the track of last state name and its $stateParams.
We can inject our service to all scopes:
.run(['$rootScope', 'PreviousState',
function ($rootScope, PreviousState) {
$rootScope.PreviousState = PreviousState;
}])
And we can use it as a click or href:
<button ng-click="PreviousState.goToLastState()">go back</button>
<a ng-href="#{{PreviousState.getLastHref()}}" > go to last href</a>
Check that in action here
Related
I am trying to redirect to home page when specific views are refreshed in browser. If I handle stateChangeStart event on rootScope level, it all works fine and navigates to home page.
var payApp = angular.module('pay', ['ui.router', 'pay.controllers', 'pay.services', 'exceptionOverride']);
payApp.run(['$templateCache', '$rootScope', '$state', '$stateParams', '$window', '$location', function ($templateCache, $rootScope, $state, $stateParams, $window, $location) {
$templateCache.put(baseUrl, angular.element('#ui-view').html());
// Allows to retrieve UI Router state information from inside templates
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
$rootScope.$on('$stateChangeSuccess', function (event, toState) {
// Sets the layout name, which can be used to display different layouts (header, footer etc.)
// based on which page the user is located
$rootScope.layout = toState.layout;
});
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams, options) {
//When payment or order page is refreshed, navigate to home
if (fromState.name == '' && (toState.name == 'payment' || toState.name == 'order')) {
$location.url('/');
}
});
var windowElement = angular.element($window);
windowElement.on('beforeunload', function (event) {
var path = $location.path();
if (path == '/payment' || path == '/order') {
event.preventDefault();
}
});
}]);
But I am trying to move this logic to a specific controller level so the stateChangeStart event will be fired only for specific views which are using this controller.
var payControllers = angular.module('pay.controllers', ['ui.bootstrap', 'ngIdle']);
payControllers.controller('payPaymentCtrl', ['$scope', '$state', '$window', '$location', 'payDataService', 'Idle', 'Keepalive', '$uibModal',
function ($scope, $state, $window, $location, service, Idle, Keepalive, $uibModal) {
var windowElement = angular.element($window);
windowElement.on('beforeunload', function (event) {
var path = $location.path();
if (path == '/payment' || path == '/paymentcreated') {
event.preventDefault();
}
});
$scope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams, options) {
//When payment or order page is refreshed, navigate to home
//Both payment and order share the same controller.
if (fromState.name == '' && (toState.name == 'payment' || toState.name == 'order')) {
$location.url('/');
}
});
$scope.paymentOption = service.GetpaymentOption();
if ($scope.paymentOption == null) {
//$state.go('home', {}, { reload: true });
//$location.path('/').replace();
$location.url('/');
}
}]);
The beforeunload event fires in the controller when a view is refreshed in browser but stateChangeStart does not fire.
Also, I tried to get rid of the stateChangeStart event from the controller level and handle to go to home page when some crucial data is missing. It gives me an error saying "Unable to load property of undefined". It appears to be trying to load some data that is bound to angular scope elements which usually appears upon some operation on home page. I need it to look like just the way when I open the home page for the first time when the application is run.
Thanks for any suggestions.
Is there a way to redirect a user to a specific state based on data from cookies when using ui-router?
I tried to do it from the .config() but since I'm not able to inject other dependencies it wasnt working.
I also tried to do it on the .run() block, but it just gives a loop (of course).
This is what I first tried on the .config()
function ConfigRouter($locationProvider, $stateProvider, $urlRouterProvider, Constant, localStorageService) {
$locationProvider.html5Mode(true);
$urlRouterProvider.when('/', function($injector) {
var $state = $injector.get('$state');
checkState($state);
});
$urlRouterProvider.when('', function($injector) {
var $state = $injector.get('$state');
checkState($state);
});
$urlRouterProvider.otherwise(function($injector) {
var $state = $injector.get('$state');
$state.go('error');
});
function checkState($state) {
var userCookie = localStorageService.cookie.get(Constant.cookieName);
if(userCookie.type == 1) {
$state.go('home-app');
} else if (userCookie.type == 2) {
$state.go('home-client');
} //and so on
}
}
Is there a way to do it? Or other way to achieve the same result? Basically I need to send the user to a different portion of the app based on the users role. If he is an admin, client, moderator, etc.. Each one has an specific app and need to retrieve specific data from server, this is why i want to do it, so i can request the data on the resolve of each state.
If you are using angular ui router, you can use resolve on the top state, there you can inject services which helps you to verify the cookie
you can also intercept and do it during
.run(["$rootScope", "$location", "$state", "services.userService", function ($rootScope, $location, $state) {
$rootScope.$on('$stateChangeStart', function (e, toState, toParams
, fromState, fromParams) {
// validation
});
more info https://github.com/angular-ui/ui-router/wiki#resolve
and examples here
angular ui-router login authentication
Defer Angular UI Router $stateChangeStart until server authorization response receieved
ej:
.state('main', {
templateUrl: 'app/modules/home/view.html',
abstract: true,
resolve: {
authorize: ['userService', "$state", "$q",
function (userService, $state, $q) {
return checkAuthorization(userService, $state, $q)
.then(function (user) {
return {user: user}
});
}]
},
controller: 'RootController'
})
// more states
var checkAuthorization = function(userService, $state){
//do all the necessary checks and return the user if he is already logged in
//redirecct to other page if the check failed
}
I did user check in the .run section:
.run(['$rootScope', '$state', '$cookies',
function ($rootScope, $state, $cookies) {
if ($cookies.get('token'))
$state.go('main');
else
$state.go('guest)
}]);
Of course than you should install 'token' in cookie. And now you don't need $urlRouterProvider in .config
I want the users not to go to certain pages at least they've logged in before. I'm currently using this:
app.run(function ($rootScope, $route, $location)
{
var restrictedPages =
[
'/Survey/', '/Survey/Detalle', '/Survey/Accepted', '/Account/Profile',
'/Auction/Detail', '/Survey/Accepted'
];
$rootScope.$on('$locationChangeStart', function (ev, next, current)
{
var nextPath = $location.path();
var nextRoute = $route.routes[nextPath];
if(restrictedPages.indexOf(nextPath) !== -1)
{
$location.path('/home');
}
});
});
My problem here is that I want to inject inside of this piece of code my AccountService. How can I achieve this? Because the loading-order is the following
app.js (the code presented is inside here)
homeService.js
accountService.js
I truly believe this is not the right way to go but it seems so simple and the only thing I'm missing is the account service injection.
Consider this module, which includes a accountService that uses implicit DI:
angular.module('myApp', [])
.factory('accountService', function($rootScope) {
// $rootScope is implicitly injected
})
.run(['$rootScope', '$route', '$location', 'accountService', function ($rootScope, $route, $location, accountService)
{
var restrictedPages =
[
'/Survey/', '/Survey/Detalle', '/Survey/Accepted', '/Account/Profile',
'/Auction/Detail', '/Survey/Accepted'
];
$rootScope.$on('$locationChangeStart', function (ev, next, current)
{
var nextPath = $location.path();
var nextRoute = $route.routes[nextPath];
if(restrictedPages.indexOf(nextPath) !== -1 && !accountService.isLoggedIn())
{
$location.path('/home');
}
});
}]);
if you want more documentation: https://docs.angularjs.org/guide/di
I'm new to angular-ui-router and I've been trying to do some unit testing for a basic authentication, its working fine until I hit a maximum call stack error.
I've narrowed the error down to the $state.go call in the app.run section.
I remove this and the test works. However it breaks my app
What can I do to resolve this? so I can test this section and make it work as well?
Why does this work normally but causes a test error?
Error:
RangeError: Maximum call stack size exceeded
at Scope.$broadcast (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular/angular.js:12876:15)
at Object.transitionTo (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2584:24)
at Object.go (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2454:21)
at /Users/paulrobinson/Workspace/contactCachePOC/dev/js/core.js:9:5889
at Scope.$broadcast (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular/angular.js:12874:28)
at Object.transitionTo (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2584:24)
at Object.go (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2454:21)
at /Users/paulrobinson/Workspace/contactCachePOC/dev/js/core.js:9:5889
at Scope.$broadcast (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular/angular.js:12874:28)
at Object.transitionTo (/Users/paulrobinson/Workspace/contactCachePOC/dev/bower_components/angular-ui-router/release/angular-ui-router.js:2584:24)
Code:
app.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise('/');
var access = routingConfig.roles;
$stateProvider
.state('start', {
url : '/',
templateUrl : 'partials/decide.html',
controller : 'decideController',
data: {
access: access.anon
}
});
}]);
app.run(['$rootScope', '$state', 'AuthService', '$log', '$location',
function ($rootScope, $state, AuthService, $log, $location) {
$rootScope.$on("$stateChangeStart",
function (event, toState, toParams, fromState, fromParams) {
if (!AuthService.isAuthorized(toState.data.access)) {
event.preventDefault();
$rootScope.error = null;
//STATE.GO is causing the error
$state.go('start');
//$location.path('/#/');
return;
}
});
}]);
describe('Test as an anonymous user', function () {
var $templateCache, $state, $stateParams, $rootScope, $httpBackend,
AuthService,, $location;
var roles = {
anon: { id: 0, value: 'Public'},
user: { id: 1, value: 'User'}
};
beforeEach(module('app'));
beforeEach(inject(function(_$templateCache_, _$state_, _$stateParams_, _$rootScope_, _$httpBackend_,
_AuthService_, _sessionService_, _$location_) {
$templateCache = _$templateCache_;
$state = _$state_;
$stateParams = _$stateParams_;
$rootScope = _$rootScope_;
$httpBackend = _$httpBackend_;
AuthService = _AuthService_;
$location = _$location_;
//Fake it and say we're not authorized.
spyOn(AuthService, "isAuthorized").andCallFake(function (state){
return false;
});
}));
describe('View page.', function () {
beforeEach(function () {
$state.go('start', { });
$rootScope.$apply();
});
it('Should view page.', function () {
expect($state.current.name).toEqual('start');
});
});
});
Maximum call stack exceeded suggests that something is happening recursively. I'd guess that the state that you are going to "start" is also not authorised, so you get redirected to "start" again, and so on.
You need to make your AuthService.IsAuthorized return true at some point
Ok so it appears to be a bug with ui-router.
Here is a solution
https://github.com/angular-ui/ui-router/issues/178#issuecomment-49156829
$state.go(toState.name, toParams, {notify: false}).then(function() {
$rootScope.$broadcast('$stateChangeSuccess', toState, toParams, fromState, fromParams);
});
The above code will deal with the problem without breaking the browser or unit tests.
Just beware of https://github.com/angular-ui/ui-router/issues/1348 when using this fix.
Use $state.go("main", {}, {notify:false}); for notify to $stateChangeStart event.
$state.go("main", {}, {notify:false});
My test has:
it("should clear the search field when the URL changes", function() {
createController();
$scope.init();
$scope.searchTerm = 'some term';
$location.path('/source');
$rootScope.$apply();
expect($scope.searchTerm).toBe('');
});
My controller is:
angularMoonApp.controller('SearchController', ['$scope', '$location', function ($scope, $location) {
$scope.init = function() {
$scope.$on('$routeChangeStart', function(next, current) {
$scope.searchTerm = '';
});
}
$scope.init();
}]);
Seems simple enough! So why won't that trigger when I Change the location in the test?
You need to inject $route, since $routeChangeStart is an event triggered by $route.
angularMoonApp.controller('SearchController', ['$scope', '$location', '$route', function ($scope, $location, $route) {
Without knowing your use case, if you just need to detect that the url changed, you can listen for $locationChangeStart instead. $locationChangeStart is fired from $location, so you would not need to inject any new dependencies.