Last few days I've been struggling with AngularJS ui-router. I am trying to fire function on every route change, but it doesn't seem to work.
I have routes like this:
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'pages/menu.html',
resolve: {
function() {
console.log('test');
}
}
})
.state('app.home', {
url: '/home',
views: {
'menuContent': {
templateUrl: 'pages/home.html',
controller: 'homeCtrl'
}
}
})
.state('app.profile', {
url: '/profile',
views: {
'menuContent': {
templateUrl: 'pages/profile.html',
controller: 'profileCtrl'
}
}
})
Why that function inside parent state isn't firing when changing route from app.home to app.profile or vice versa?
Inside menu.html I navigate to state directly with href="#/app/home
I believe the parent controller code is being ran once when the app is first loaded.
If you want to something to run every time you change a state (I assume that's what you mean by route change), you can use an event like $stateChangeSuccess inside the parent controller.
Here's an example:
$scope.$on('$stateChangeSuccess', function (event, toState, toParams, fromState, fromParams) {
if (toState.name === 'app.about') {
console.log('This is the about state!');
}
}
You could call the function you want inside this listener and then it should fire every time.
Related
I am trying to use resolve and conditional routing in angular ui-router but it doesnt seem to work
App.js:
app.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'templates/home.html'
})
.state('app.login', {
url: '/login',
views: {
'menuContent': {
templateUrl: 'templates/login.html'
}
}
})
.state('app.therapist', {
url: '/therapist',
views: {
'menuContent': {
templateUrl: 'templates/therapist.html'
}
}
}).state('app.trial', {
url: '/trial',
views: {
'menuContent': {
templateUrl: 'templates/trial.html'
}
},
resolve:{
check:function($state){
if(1==1){
e.preventDefault();
$state.go('app.login',{}) ; /**/Here trying**
}else{
return true;
}
}
}
$urlRouterProvider.otherwise('/app/therapist');
});
So in app.trail route I was testing and trying to redirect to app.login but it doesnt work it only opens the trial page
In e.preventDefault(); you appear to be missing anything defined as e.
You can use preventDefault() when handling an event such as $stateChangeStart, but I don't think that is available within resolve.
See Angular ui-router $state.go is not redirecting inside resolve for some suggestions on how to handle this (do the check inside a handler for $stateChangeStart, or send an event to trigger the transition, or use a timeout to delay your state change until after the first one has completed).
I have the following routes, using Angular ui-router.
If I navigate to /3872 that gives me a user with that ID and the correct template, that's great.
But the problem is, if I type an ID that doesn't exist, such as /1111 I want to redirect to the root. At the moment it stays on the user route and gives me an empty template instead.
$stateProvider
.state('root', {
url: '/',
templateUrl: 'home.html',
controller: 'home_controller'
})
.state('user', {
url: '/:userId',
templateUrl: 'user.html',
controller: 'user_controller'
});
$urlRouterProvider.otherwise('/');
One way is to use $stateChangeStart event to make some filtering. That goes into app.run(...) section
$rootScope.$on('$stateChangeStart', function (event, toState, toParams, fromState, fromParams) {
if (toState.name == "user") {
//some logic... and most probably
//event.preventDefault();
//$state.go(toState.name, toParams);
}
});
In user_controller($stateParams,$state):
if($stateParams.userId not in userIds) {
$state.go('root');
}
I use angular's ui-router and nested routes in a project and I'm faced by the problem that everything works like a charm when I use links (ui-sref) to navigate to the user's detail page with the userId as part of the url. When I refresh the page, state params are missing.
So I've taken a look at the angular demo: http://angular-ui.github.io/ui-router/sample/#/contacts/1/item/b and couldn't reproduce this behaviour, however nested states are not part of this demo in contrast to my application.
$stateProvider
.state('base', {
abstract: true
})
.state('home', {
parent: 'base',
url: '/',
views: {
'content#': {
templateUrl: 'app/index/index.view.html',
controller: 'IndexController',
controllerAs: 'home'
}
}
})
.state('users', {
url: '/users',
parent: 'base',
abstract: true
})
.state('users.list', {
url: '/list',
views: {
'content#': {
templateUrl: 'app/users/users.list.html',
controller: 'UsersController',
controllerAs: 'users'
}
},
permissions: {
authorizedRoles: UserRoles.ALL_ROLES
}
})
.state('users.details', {
url: '/:userId/details',
views: {
'content#': {
templateUrl: 'app/users/user.details.html',
controller: 'UserDetailsController',
controllerAs: 'userDetails'
}
},
resolve: {
logSomeParams: ['$stateParams', '$state', function ($stateParams, $state) {
console.log($stateParams);
console.log(this);
console.log($state);
}]
}
})
When refreshing the page the url immediately changes to http://localhost:3000/#/users//details and console output (resolve function) shows that params are missing.
html5Mode (LocationProvider) is not enabled. I already found "solutions" like redirecting back to the list if the the userId is missing on page refresh, but I just can't believe that there isn't a better way to solve this.
This is how I linked the details page in the overview (and it is working):
<div class="panel-body" ui-sref="users.details({userId: user.siloUserId})">
As expected, my problem had nothing to do with the ui-router. The URLMatcher works as expected, even if you refresh the page (everything else would have been a huge dissapointment).
However, I have a $stateChangeStart listener which checks if the SAML authentication session (cookie) is still valid and waits for the result.
function(event, toState, toParams, fromState, fromParams){
if(!authenticationService.isInitialized()) {
event.preventDefault();
authenticationService.getDeferred().then(() => {
$state.go(toState);
});
} else {
//check authorization
if ( authenticationService.protectedState(toState) && !authenticationService.isAuthorized(toState.permissions.authorizedRoles)) {
authenticationService.denyAccess();
event.preventDefault();
}
}
}
No idea how this could happen, but I forgot to pass the parameters to the go method of the stateProvider. So all I had to change was to add the params:
$state.go(toState, toParams);
Currently I have a state 'home' which resides at the url '/'. After a user connects, the 'client' state is loaded, which also resides at the url '/' and the session is updated. Is it possible to make it so that if the user reloads the page, the 'client' state is loaded immediately instead of the 'home' state? If they were at different urls, I could simply have the server perform a redirect but I would like them both to be at the base url '/'. Is there some functionality in ui-router that would let me achieve this?
Here are my states:
app.config(function($stateProvider, $urlRouterProvider,$locationProvider) {
$stateProvider
.state('home', {
url: '/',
controller: 'HomePageConroller as homeCtrl',
templateUrl: '/views/partials/home.html',
onEnter: function($http,$state) {
$http.post('/checkconnected')
.success(function(data,status) {
if (data) {$state.go('client');}
});
}
})
.state('client', {
url: '/',
controller: 'ClientController as clientCtrl',
templateUrl: '/views/partials/client.html'
});
As you can see I am currently posting a check to the server to see if the client is connected and then doing a redirect from within the angular application, but I am not happy with this solution at all. The post takes time and so the home page is fully loaded and seen by the user before the client page is shown.
One solution I have considered is having another state called 'client redirect' that does reside at '/client' and has the same template as the 'client' state, which does the same $state.go on enter without the need of an $http.post (because the server already redirected based on the updated session). This would remove the delay and not flash the homescreen, however it does not seem elegant.
var app = angular.module("app", [
"ui.router"
]);
app.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/index');
$stateProvider
.state("app", {
abstract: true,
url: '/app',
templateUrl: "views/layout/app-body.html"
})
.state("index", {
url: "/index",
templateUrl: "views/home.html",
controller: 'signinController'
})
.state("signin", {
url: "/signin",
templateUrl: "views/pages/signin.html",
controller: 'signinController'
})
.state("signup", {
url: "/signup",
templateUrl: "views/pages/signup.html",
controller: 'signupController'
})
.state('app.home', {
url: '/home',
templateUrl: 'views/home/home.html',
controller: 'homeController'
})
.state('app.dashboard', {
url: '/dashboard',
templateUrl: 'views/dashboard/dashboard.html',
controller: 'dashboardController'
})
.state('app.profile', {
url: '/profile',
templateUrl: 'views/pages/profile.html',
controller: 'profileController'
});
});
Visit http://embed.plnkr.co/IzimSVsstarlFviAm7S7/preview?
Okay here is how I solved this. I renamed the url of 'client' from '/' to '/client'. On my server the get request checks if the user is connected and redirects to get '/client' if so (which renders the exact same angular app only the url is different). Then I modified my client to state to onEnter change $location.path(). Realizing this is practically the same as $state.go, I used the event handler for $stateChangeStart to prevent the state transition. Final code looks like this:
app.config(function($stateProvider, $urlRouterProvider,$locationProvider) {
$stateProvider
.state('home', {
url: '/',
controller: 'HomePageConroller as homeCtrl',
templateUrl: '/views/partials/home.html'
})
.state('client', {
url: '/client',
controller: 'ClientController as clientCtrl',
templateUrl: '/views/partials/client.html',
onEnter: function($location){$location.path('/');}
});
$urlRouterProvider.otherwise('/');
$locationProvider.html5Mode(true);
});
app.run(['$rootScope', function($rootScope) {
$rootScope.$on('$stateChangeStart', function(event, toState, toParams, fromState, fromParams){
if (fromState.name == 'client' && toState.name == 'home') {
event.preventDefault();
}
});
}]);
If I can clean this up further please comment below.
I have these router.js inside my ionic app ,
myapp.config(function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('app.myprofile', {
url: "/myprofile",
abstract: true,
views: {
'menuContent': {
controller : "myprofileCtrl",
templateUrl: "templates/myprofile.html"
}
}
})
.state('app.myprofile.info', {
url: "/info",
controller: "profileinfoCtrl",
templateUrl: "templates/profile/info.html"
})
.state('app.myprofile.location', {
url: "/location",
controller: "profilelocationCtrl",
templateUrl: "templates/profile/location.html"
})
$urlRouterProvider.otherwise('/');
});
I need a way to share controller scopes between myprofile state and myprofile.location state and myprofile.info state too .
I am using ionic framework
in myprofileCtrl
myapp.controller("myprofileCtrl",function($scope,Authy ,Auth,$http ,$rootScope){
$scope.view_loading = true;
Authy.can_go().then(function(data){
$scope.profile =data.profile;
});
});
in profilelocationCtrl
myapp.controller("profilelocationCtrl",function($scope,Authy ,Auth,$http ,$rootScope){
console.log($scope.profile);
});
I got undefined in the console
I think the problem is that since $scope.profile is set after an ajax call, when the myprofile.location state is reached initially, then profile variable is still undefined on the parent $scope, what you could do is $watch the profile variable, or better just broadcast an event downwards when you receive it fro the server call using $broadcast.
$broadcast dispatches the event downwards to all child scopes, so you could do:
myapp.controller("myprofileCtrl",function($scope,Authy ,Auth,$http ,$rootScope){
$scope.view_loading = true;
Authy.can_go().then(function(data){
$scope.profile =data.profile;
$scope.$broadcast('profileSet', data.profile);
});
});
and in the myprofile.location` state:
myapp.controller("profilelocationCtrl",function($scope,Authy,Auth,$http,$rootScope){
$scope.$on('profileSet', function(event, data) {
console.log(data);
});
}
});
Have a look at this demo plunk I just made.