In my AngularJS application in run() function I want to send user to signin page if they are not logged in. Here is code:
.run(['$state', function($state)
{
$state.go('signin')
.then(function(promise){},
function(reject)
{
console.log(reject)
}
);
}]);
In console I get following:
Error: transition superseded {stack: (...), message: "transition superseded"}
Neither $state.transitionTo('signin') doesn't work. Does anyone have idea why this happens, I've never encountered this.
I think you should be checking for authentication in the $stateProvider instead of run(). You could do something like this instead:
$stateProvider
.state('auth', {
abstract: true,
resolve: {
user: ['auth', '$q', function (auth, $q) {
if (auth.isAuthenticated()) {
//...
} else {
//...
}
}]
}
})
I think your problem might be that the state is still not loaded so you cant go to that state...
a solution would be to set timeout 0 so it runs this after the stack is empty.
try this:
.run($timeout, $state, ...) {
$timeout(function() {
$state.go('signin');
},0);
or something like that
referanced from :
module.run() and $state.go() angularJS
Related
I am using ui-router for routing in my angularjs app and ui-bootstrap for UI.In my app on entering a state i am opening a uibmodal which basically returns a uibmodalinstance but when i change a state using
$state.go('dashboard')
Inside my controller it is changing the state but didn't closing modal.
So i want modal to be closed on exiting the state.
i Have written following code but some part of code doesn't work.
please see coding and the comments for not working part
$stateProvider.state('makeabid',{
parent: 'dashboard',
url: '/makeabid/{id}',
data: {
authorities: ['ROLE_USER'],
pageTitle: 'global.menu.makeabid'
},
onEnter: ['$stateParams', '$state', '$uibModal', function($stateParams, $state, $uibModal) {
$uibModal.open({
templateUrl: 'app/dashboard/makeabid/makeabid.html',
controller: 'MakeabidController',
controllerAs: 'vm',
backdrop: true,
size: 'lg'
}).result.then(function () {
$state.go('dashboard');
});
}]
//this part doesnt work
,onExit:['$uibModalInstance','$stateParams', '$state',function ($uibModalInstance,$stateParams, $state) {
$uibModalInstance.close();
}]
});
My Controller Coding is as follows : -
MakeabidController.$inject = ['$stateParams','$state','$uibModalInstance','MakeabidService'];
function MakeabidController( $stateParams, $state, $uibModalInstance, MakeabidService) {
var vm = this;
loadAll();
vm.clear = clear;
vm.save = save;
function clear () {
$uibModalInstance.close();
}
function save() {
// console.log(vm.comparableData);
}
function loadAll() {
vm.comparableData = MakeabidService.getobject();
if(angular.isUndefined(vm.comparableData)){
//$uibModalInstance.close(); //It doesn't work
$state.go('dashboard'); //This is working
}
}
}
AnyOne Please Tell me solution for closing the uibmodal on changing state
I solved it by adding $uibModalStack.close() in my app.run
function run($uibModalStack) {
$rootScope.$on('$stateChangeStart', function() {
$uibModalStack.dismissAll();
});
}
You can tap into to some of the $stateProvider events.
I do something similar in one of my apps (in coffeescript, but you get the idea)
#$scope.$on '$stateChangeStart', (e, to, top, from, fromp) =>
#$uibModalInstance.close()
Basically, in your controller that handles the modal, you will watch for the $stateChangeStart event, and when you catch it, you can close the modal.
See https://github.com/angular-ui/ui-router/wiki, specifically the section on State Change Events
---EDIT---
I just noticed that these calls are deprecated. If you are using UI-Router > 1.0, there is some documentation here on how to migrate: https://ui-router.github.io/guide/ng1/migrate-to-1_0#state-change-events
I've been doing some Googling around this already but I'm unable to find a solution that works.
I'm using AngularJS 1.5.5 and .NET Web API 2 to build a web application and I would quite simply like to hide the ng-view element until all resolves have completed on the route.
I'm trying to use the $routeChangeStart and $routeChangeSuccess to set a variable on the $rootScope that is used in the index html to display the loading indicator and hide the content until the variable is false.
Here is my routing code for the routeChange properties:
_app.config([
'$routeProvider', '$httpProvider', '$provide',
function ($routeProvider, $httpProvider, $provide) {
$routeProvider.when('/Account',
{
templateUrl: '/Content/js/areas/account/account.html',
controller: 'accountController',
resolve: {
$accountResolver: function (accountService) {
return accountService.getMyAccountData();
}
},
caseInsensitiveMatch: true
});
$routeProvider.otherwise({ redirectTo: '404' });
}
]);
_app.run(['$rootScope', '$location', '$window', '$q', 'authService',
function ($rootScope, $location, $window, $q, authService) {
$rootScope.$on("$routeChangeStart",
function (e, curr, prev) {
$rootScope.$loadingRoute = true;
});
$rootScope.$on("$routeChangeSuccess",
function (evt, next) {
$rootScope.$loadingRoute = false;
});
$rootScope.$on("$routeChangeError",
function (evt, next) {
$rootScope.$loadingRoute = false;
});
}]);
And here is my html using that $loadingRoute variable:
<body class="ng-cloak" data-ng-app="wishlist" data-ng-controller="appController">
<wl-header></wl-header>
<preloader ng-if="$loadingRoute"></preloader>
<section ng-view ng-if="!$loadingRoute" class="container ng-cloak"></section>
</body>
I understand that there's quite a lot of articles covering this but none seem to work in my case. $loadingRoute gets set to true when the route change starts, as expected, which I will see if I add {{$loadingRoute}} to the HTML before the <section></section> tag. However before the $accountResolveris resolved, the $routeChangeSuccess gets fired, setting $rootScope.$loadingRoute = false which is unexpected.
I was under the impression that $routeChangeSuccess only got fired after all resolves had completed on the current route.
Am I doing something really obviously wrong here? Or has Angular simply changed?
Edit: I would also like to add that this approach worked in previous projects, so I'm at a real loss as to what's going wrong. I could set $rootScope.$loadingRoute manually in each page controller but that feels too dirty and unmaintainable.
Edit 2:
_app.factory('accountService', [
'accountResource',
function (accountResource) {
var _self = this;
return {
register: function (authData) {
return accountResource.register(authData);
},
getMyAccountData: function () {
return accountResource.getMyAccountData();
}
}
}
]);
_app.factory('accountResource', [
'$resource', 'rootUrl',
function ($resource, rootUrl) {
var api = rootUrl() + 'api/Account';
return $resource(api,
{},
{
register: {
method: 'POST',
url: '{0}/register'.format(api)
},
getMyAccountData: {
method: 'GET',
url: '{0}/GetMyAccountData'.format(api)
}
});
}
])
In order for a resolver to delay route change, it should return a promise. Otherwise route change happens immediately, this is what happens when $routeChangeSuccess is triggered before a promise from accountService.getMyAccountData() is resolved.
The problem is $resource methods (and so accountService.getMyAccountData()) return self-filling object that is populated with data asynchronously. A promise for this data is available as $promise property (see the reference), so it should be used for a resolver:
$accountResolver: function (accountService) {
return accountService.getMyAccountData().$promise;
}
If accountService is supposed to be purely promise-based wrapper for accountResource, a cleaner way to do this is to return a promise from its methods instead:
getMyAccountData: function () {
return accountResource.getMyAccountData().$promise;
}
I have spent the entire day on this (hobby-programmer, not a real one). I admit up front that the issue is my lack of understanding of the basic fundamentals of angular (and most programming for that matter). I am especially new to web development and need some help.
Anyways, I have a template that I'm using for learning purposes, that's all this is really. It's the 'ani-theme' from startangular.com. I built some basic logic to authenticate a user (type 'aaa' in the email lol, remember, its just for learning). This code works fine, and if 'aaa' is entered then the router will be triggered to move you to the dashboard.
The problem is that the user could just put the URL for the dashboard in the browser and go there.
I have a variable called "authed" that is set to true once they log in, but I cant seem to 'conditionally route' the user to the dashboard when they type the URL in manually. I have tried so many things but no luck.
After 5 hours of research, i think it is due to the asynchronous nature of angular OR scope issues. Or both. Probably neither. idk.
I saw so many other posts about $stateChangeStart but it went way over my head. Can someone point me in the right direction here or try to explain what is going on here in dummy terms. I mean it, I really don't know much so dumb it down, I wont be insulted.
APP.JS
var authed = false;
var done = false;
var yapp = angular
.module('yapp', [
'ui.router',
'ngAnimate',
'myAuth'
])
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.when('/dashboard', '/dashboard/overview');
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('base', {
abstract: true,
url: '',
templateUrl: 'views/base.html'
})
.state('login', {
url: '/login',
parent: 'base',
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
})
.state('dashboard', {
url: '/dashboard',
parent: 'base',
templateUrl: 'views/dashboard.html',
controller: 'DashboardCtrl'
})
.state('overview', {
url: '/overview',
parent: 'dashboard',
templateUrl: 'views/dashboard/overview.html'
})
.state('reports', {
url: '/reports',
parent: 'dashboard',
templateUrl: 'views/dashboard/reports.html'
});
});
LOGIN.JS
angular.module('yapp')
.controller('LoginCtrl', function($scope, $location, authFactory) {
$scope.submit = function(emailp) {
if(authFactory.checkAuth(emailp)) {
$location.path('/dashboard');
}else{
alert("WRONG");
}
}
});
AUTH.JS
var myAuth = angular.module('myAuth', [])
.factory('authFactory', function(){
var factory = {};
factory.checkAuth = function(emailp){
if(emailp == 'aaa') authed = true;
return(authed);
};
return factory;
});
IMPORTANT SIDE NOTE
I love advice and help, so please, if you see other things I'm doing that just look ridiculous, please call me out. It will help me a lot.
------------------------------------------------
EDIT EDIT EDIT EDIT EDIT
Thanks for the answers so far! I am going to try implementing #swestner 's answer and once it is working, I will study the 'why' part so I can really understand.
I do have another question on this same issue to clarify so that I can better understand why my other method wasn't working. I am very curious because it is a strange behavior.
So, You see my authed variable is declared in app.js, and then in the auth.js factory it is set to true or false depending on the users 'emailp'.
I added some logic to the app.js saying 'if authed is true, use these route commands, otherwise use these ones.
example:
console.log(authed) //this actually does print the correct value...
if(authed) { //however this doesnt work!
$urlRouterProvider.when('/dashboard', '/dashboard/overview');
$urlRouterProvider.otherwise('/login');
}else{
$urlRouterProvider.when('/dashboard', '/login');
$urlRouterProvider.otherwise('/login');
}
I can print the correct value, however the condition is always true.
HOWEVER, if I declare the variable authed2 RIGHT before the conditional statement it works fine!
var authed2 = true;
console.log(authed + " #1");
console.log(authed2 + ' #2');
if(authed2) {
$urlRouterProvider.when('/dashboard', '/dashboard/overview');
$urlRouterProvider.otherwise('/login');
}else{
$urlRouterProvider.when('/dashboard', '/login');
$urlRouterProvider.otherwise('/login');
}
The program knows both values to be true, I can even print them both right before the conditional, however when I use authed (set and declared elsewhere) the conditional doesnt work (even tho it seems to know the answer).
Its confusing me and I have to be missing some background behavior here.
The $stateChangeStart event is the proper place to handle this. This event will fire when you try to navigate to a url. At that point you can check if the user is authenticated, and if not, bounce them back to login.
You would hook up the event like this :
angular
.module('yapp')
.run(function ($rootScope, $state, authFactory) {
$rootScope.$on('$stateChangeStart', function () {
if(!authFactory.isAuthed()){
$state.go('login')
}
})
});
And update your auth factory to have the isAuthed method.
var myAuth = angular.module('myAuth', [])
.factory('authFactory', function () {
var factory = {};
factory.checkAuth = function (emailp) {
if (emailp == 'aaa') authed = true;
return (authed);
};
factory.isAuthed = function () {
return authed;
}
return factory;
});
I have the following question... or situation. I have states defined in my AngularJS app, like so...
$stateProvider
.state('myApp', {
abstract: true,
template: '<ui-view/>'
})
.state('myApp.stateOne', {
url: 'state1',
templateUrl: '/an/views/state-1.html',
controller: 'StateOneCtrl'
})
.state('myApp.stateTwo', {
url: 'state2',
templateUrl: '/an/views/state-2.html'
controller: 'StateTwoCtrl'
})
.state('myApp.stateThree', {
url: 'state3',
templateUrl: '/an/views/state-3.html'
controller: 'StateThreeCtrl'
})
There are more states and I have changed the naming for this example, but suppose I need to check if the user is allowed to see / load 'mayApp.stateThree'. I can determine this by asking the backend. I have a service (in this example called IsAllowedService) to deal with this requests / provide the access and normally I would write the logic to do the check in the .run() block in my app.js file for example:
.run(['IsAllowedService', '$state', function (IsAllowedService, $state) {
$rootScope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState) {
// check if we are going to sfm.addContacts and if we are allowed to...
if (toState.name === 'myApp.stateThree') {
IsAllowedService.checkIfIsAllowed().then(function (resp) {
if(resp.allowed === false) {
$state.go('myApp.stateOne');
}
});
}
});
}]);
This works well but doesn't wait until we get the result from the service so 'mayApp.stateThree' is loaded then we a redirected if necessary. So we get a quick flash of the page before we are redirected. I could put the same code into the 'StateThreeCtrl' but I still get the flash / FOUC. Would it be possible to resolve this when defining the states, I know this won't work but something like this...
.state('myApp.stateThree', {
url: '/an/state3',
templateUrl: '/an/views/state-3.html'
controller: 'StateThreeCtrl',
resolve: {
isAllowed : function () {
IsAllowedService.checkIfIsAllowed().then(function (resp) {
return resp;
})
}
}
I realise that I wouldn't be able to inject the service (or even the $http service) but is it possible for me to somehow pause the loading of the view / controller of 'mayApp.stateThree' until I get the result from IsAllowedService.checkIfIsAllowed(). Any advice on how to structure my app / code would be appreciated. I have used ng-cloak in my HTML view but this did nothing!
Actually you're doing it almost right in the application's run block. Except you are not preventing anything. You can achieve that by adding:
event.preventDefault(); //Prevent from going to the page
Furthermore, you can add custom data to your $states , which will allow you to verify those conditions with your criteria. e.g.:
$stateProvider.state('home', {
controller: 'HomeController as home',
url: '/home',
templateUrl: 'home.html',
data: { roles: [ROLES.ANONYMOUS] }}); //This can be any condition
$stateProvider.state('user', {
controller: 'UserController as user',
url: '/user',
templateUrl: 'user.html',
data: { roles: [ROLES.ADMIN, ROLES.USER] }});
You can retrieve this custom data in the $stateChangeStart event:
$rootScope.$on('$stateChangeStart', function (event, next) {
if (!yourService.isAuthorized(next.data.roles)) {
event.preventDefault(); //Prevent from going to the page -> no flickering
$state.go('403'); //Or whatever is desired.
}
});
You see the flickering because you're using a Promise and the first page only gets redirected when the promise is furfilled. You can stop the flickering by preventing the default action, authorize and continue your flow as you desire when the promise resolves.
if (toState.name === 'myApp.stateThree') {
event.preventDefault(); //preventing the request.
IsAllowedService.checkIfIsAllowed().then(function (resp) {
if(resp.allowed === false) {
$state.go('myApp.stateOne');
} else { //he actually is allowed to go to state three.
$state.go('myApp.stateThree');
}
}, function() { //in case the server has no answer
$state.go('myApp.stateOne'); //you probably want to prevent it too
} );
In my opinion, if these conditions do not change during runtime, i.e. user role based, you can retrieve them upon user verification so you don't need a promise to begin with. Hope this helps.
I made a similar post before and added a working plunker.
I'm struggling to get Angular route resolve working. I find the documentation less than useless for more complex parts of the javascript framework like this.
I have the following service:
app.service("AuthService", ["$http", "$q", function($http, $q){
this.test = function(){
return $q(function(resolve, reject){
var auth = false;
resolve(auth);
});
}
}]);
Now my routes looks like this:
app.config(["$routeProvider", "$locationProvider", function($routeProvider, $locationProvider){
$locationProvider.html5Mode(true).hashPrefix("!");
$routeProvider
.when("/account", {
templateUrl: "/views/auth/account.html",
controller: "AccountController",
resolve: {
auth: ["AuthService", function(AuthService) {
return AuthService.test().then(function(auth){
if (auth) return true;
else return false;
});
}]
}
});
}]);
What I want to happen here is the following:
User goes to /account
AuthService is fired and returns a variable (true or false)
In the resolve, if the returned value is false, the route cannot be loaded
If the returned value is true, the user is authenticated and can view the route
I don't think I've fully understood how to use resolve and my method so far does not work. Could someone please explain the correct way to do this?
The resolve block when configuring routes is designed to make navigation conditional based upon the resolution or rejection of a promise.
You are attempting to handle this by resolving with a resolution value of true or false
Please try the following:
resolve: {
auth: ["AuthService", function(AuthService) {
return AuthService.test().then(function(auth){
if (!auth){
throw 'not authorized';
}
});
}]
}
This will cause the promise to be rejected and therefore not allow the routing to continue/complete.
Also of note is that the value coming out of the promise resolution will be injected into the handling controller
This solution works for me, I also tried with .then function but it's incorrect because resolve performs it.
resolve: {
auth:
["AuthService", "AnotherService", "$rootScope", function(AuthService, AnotherService, $rootScope) {
if ($rootScope.yourCondition){
return AuthService.getFunction();
}
else{
return AnotherService.getAnotherFunction();
}
}]
},
views: {
'partialView#': {
/* == Component version == */
component: "yourComponent",
bindings: {
auth: 'auth', // Inject auth loaded by resolve into component
params: '$stateParams'
}
}
}