How to access data from my controller in a state - angularjs

What is the best way to stop a user from going to a state if there is no data for that state? I need to redirect the user to /featured if there is no data in one of the other states.
I thought that I could check the controller for data first and then redirect using $state.go if the data was null, but I can't find an example of how to access data in a controller inside of an onEnter event
I think a code example should sum it up:
fseControllers.config(function($stateProvider, $urlRouterProvider){
$urlRouterProvider.otherwise("/featured");
$stateProvider
.state('featured', {
url: "/featured",
templateUrl: "featured-template"
})
.state('friends', {
url: "/friends",
templateUrl: "friends-template",
controller: 'FseController',
onEnter: function(){
// Here I want to redirect to /featured if no data in controller
}
})
.state('stories', {
url: "/stories",
templateUrl: "stories-template"
})
.state('events', {
url: "/events",
templateUrl: "events-template"
})
})

You could refactor your controller to use a service , that can be injected into onEnter function and to the Controller.
AngularJS Services
onEnter:function(FriendsService, $location){
if(!!FriendsService.getFriends()){
$location.path("/featured");
}
}

Related

$state.go on onEnter function of an abstract state

I have the following UI Router configuration witch includes a login page (/login), an abstract page that will be the parent of all my admin pages (/admin) and a list page that extends from the abstract page (/admin/list):
$stateProvider
.state('login', {
url: '/login',
templateUrl: '/pub/login.html',
controller: 'LoginController',
controllerAs: '$loginCtrl',
onEnter: function () {
console.log("enter /login");
}
})
.state('admin', {
abstract: true,
url: '/admin',
templateUrl: '/tmpl/templateAdmin.html',
onEnter: function () {
console.log("enter admin abstract");
// Validate JWT here and if not authenticated forward to login
$state.go('login');
}
})
.state('admin.list', {
url: '/list',
templateUrl: '/prv/clientsList.html', // loaded into ui-view of parent's template
controller: 'AppController',
controllerAs: '$appCtrl',
onEnter: function () {
console.log("enter admin/list");
}
})
I want to implement a logic that will validate if the user is authenticated when accessing an /admin/* page. In order to do that I added an onEnter function in my abstract state so that I could use $state.go('login'). But this doesn't work. How can I do this? Is there a better way to validate authentication inside UI Router?
In this case, I think it's better if you used resolve instead of onEnter.
Mainly because you want to check if the user is authenticated before navigation. If the objects inside the block don't resolve then the controller passed to the state won't be instantiated.
.state('admin', {
abstract: true,
url: '/admin',
templateUrl: '/tmpl/templateAdmin.html',
resolve: {
currentUser: function (authService) {
return authService.getUser();
}
}
}
The root state would try and resolve the user for all child views nested inside.
Here's the link to the docs on Nested States and Inherited Resolved Dependencies:
https://github.com/angular-ui/ui-router/wiki/Nested-States-&-Nested-Views

angular ui-router- ajax call on load

We are developing an single page application using angular JS and I am using state provider for configuring routes. Basically there is a global navigation view and a dashboard view. I have to pass few params from navigation to make a service call and then display the dashboard accordingly.I have split the states as two, one for navigation and other for dashboard. THe thing which i am not able to figure out is that where should i make ajax call to fetch dashboard data. Should i make it in navigation itself and pass it through resolve. or should i just pass the data to dashboard controller and make ajax call from there. Below is my state
$stateProvider
.state('home', {
url: '/',
templateUrl: 'templates/home.htm',
controller: 'homeController',
})
.state('dashboard', {
url: 'contact',
templateUrl: 'templates/dashboard.htm',
controller: 'dashboardController'
})
.state('state3', {
url: '/articles',
templateUrl: 'templates/state3.htm',
controller: 'state3Controller'
});
$urlRouterProvider.otherwise('/home');
This entirely depends on how you want the user experience to play out.
If you want to do all the data fetching before transitioning to the dashboard state, use a resolve state configuration
.state('dashboard', {
url: '/contact',
templateUrl: 'templates/dashboard.htm',
controller: 'dashboardController',
resolve: {
someData: function($http) {
return $http.get('something').then(res => res.data);
}
}
}
then your controller can be injected with someData, eg
.controller('dashboardController', function($scope, someData) { ... })
This will cause the state transition to wait until the someName promise has been resolved meaning the data is available right away in the controller.
If however you want to immediately transition to the dashboard state (and maybe show a loading message, spinner, etc), you would move the data fetching to the controller
.controller('dashboardController', function($scope, $http) {
$scope.loading = true; // just an example
$http.get('something').then(res => {
$scope.loading = false;
$scope.data = res.data;
});
})

Passing parent scope variables to ui-view component

I'm using angular-ui/ui-router and have directives/components with isolate scope defined to be rendered on state change.
$stateProvider
.state('about', {
url: '/about',
template: '<about></about>'
})
.state('blog', {
url: '/blog',
template: '<blog></blog>'
})
My main page is a directive that loads the user with all permissions in its controller
app.directive('main', function() {
return {
...
template: "<div><header></header><div ui-view></div></div>",
controller: [..., function(...) {
$scope.user = // $http load user
}]
);
When navigating to different states, I want the user to be passed to all ui-view directives/components. However I can't find a nice way to realize this without a resolve that would load the user on every state change or using the $rootScope
I'm looking for something similar to the following, where user is the one from the parent scope.
$stateProvider
.state('about', {
url: '/about',
template: '<about user="user"></about>'
})
.state('blog', {
url: '/blog',
template: '<blog user="user"></blog>'
})
Any idea how this can be done?
in the controller, you can assign the user to $rootScope, like $rootScope.user = sth, then you can whether add controller for each state url to get the user like $scope.user= $rootScope.user, or pass the rootScope user as parameter.
you need to use $rootscope in your controller like this
$rootScope.user = sth;
and you can get user like this
$scope.user= $rootScope.user;

Angular ui-router... Display default tab

I am arriving on bookDetails state form some other link. Here bookDetails state's template has links for different tabs (or templates). And associated controller EditBookController has a json file using which I am building forms in different tabs with states like bookDetails.basic and bookDetails.publisher which use parent EditBookController. It's working fine. How to directly display the default bookDetails.basic instead of making user click the link? If I make bookDetails abstract(abbstract:true) and provide an empty link to bookDetails.basic I get following error Cannot transition to abstract state 'bookDetails'
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url:'/home',
controller: 'HomeController',
templateUrl: '/static/publisher/views/Publisher_Home_Template.html'
})
.state('books', {
url:'/books',
controller: 'BooksController',
templateUrl: '/static/publisher/views/Book_Listing_Template.html'
})
.state('bookDetails', {
url : '/books/:b_id',
controller: 'EditBookController',
templateUrl: '/static/publisher/views/Product_Page_Template.html'
})
.state('bookDetails.basic', {
url : '/basic',
templateUrl: '/static/publisher/views/tab1.html'
})
.state('bookDetails.publisher', {
url : '/publisher',
templateUrl: '/static/publisher/views/tab2.html'
})
A plunk with similar problem. but code is different On clicking form it should land on the profile profile form.
I created working example here
There is similar question: Redirect a state to default substate with UI-Router in AngularJS
The solution comes from a cool "comment" related to an issue with redirection using .when() (https://stackoverflow.com/a/27131114/1679310) and really cool solution for it (by Chris T, but the original post was by yahyaKacem)
https://github.com/angular-ui/ui-router/issues/1584#issuecomment-75137373
In the state definition I added ONLY one setting to bookDetails state, the: redirectTo: 'bookDetails.basic',. Let's have a look:
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url:'/home',
controller: 'HomeController',
templateUrl: '/static/publisher/views/Publisher_Home_Template.html'
})
.state('books', {
url:'/books',
controller: 'BooksController',
templateUrl: '/static/publisher/views/Book_Listing_Template.html'
})
.state('bookDetails', {
// NEW LINE
redirectTo: 'bookDetails.basic',
url : '/books/:b_id',
controller: 'EditBookController',
templateUrl: 'static/publisher/views/Product_Page_Template.html'
})
.state('bookDetails.basic', {
url : '/basic',
templateUrl: '/static/publisher/views/tab1.html'
})
.state('bookDetails.publisher', {
url : '/publisher',
templateUrl: '/static/publisher/views/tab2.html'
})
And now - only these few lines will do the miracle:
app.run(['$rootScope', '$state',
function($rootScope, $state) {
$rootScope.$on('$stateChangeStart',
function(evt, to, params) {
if (to.redirectTo) {
evt.preventDefault();
$state.go(to.redirectTo, params)
}
}
);
}]);
This way we can adjust any of our states with its default redirection...Check it here
From Directing the user to a child state when they are transitioning to its parent state using UI-Router:
Either change the bookDetails.basic state to:
.state('bookDetails.basic', {
url : '',
templateUrl: '/static/publisher/views/tab1.html'
})
Or add the following routing:
$urlRouterProvider.when('/books/{b_id}', '/books/{b_id}/basic');
Try to add $state.go('bookDetails.basic') inside EditBookController. If I understood you< this will help.

undefined stateParams using ui-router

Question: For some reason I can't get my controller to recognize my url parameters across sessions.
Background: I have a nested view called modal that takes a parameter, whose url is /modal/:id (eg: /#/modal/1/ or /#/floorplan/1+2/). Ideally, when the user goes to this url, a modal will automatically open with the resource(s) with the given id.
Since the parent state and the child state(modal) are being handled by the same controller, the modal state has a custom data attribute (modalStatus) in its configuration set to true. When this custom attribute is enabled the modal is displayed.
I can currently go from the parent state to the nested state and trigger the modal but when I start a new session or refresh the page with a url like /modal/3, the application fails to read the parameters ($stateParams), which is being logged as an empty object.
I have tried using onEnter and Resolve but I'm not exactly clear on how to use them in this scenario.
Router
$stateProvider
.state('home', {
name: 'home',
url: '/',
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.state('home.modal', {
url: 'modal/:id/',
data: {
modalState: true
},
controller: 'MainCtrl'
})
Relevant part of controller:
$scope.init = function() {
console.log($stateParams);
if ($state.current.data) {
if ($state.current.data.modalState === true) {
$scope.openModal();
}
}
};
$scope.init();
edit: plunkr
You could try adding the params option:
$stateProvider
.state('home', {
name: 'home',
params: {
id: null
},
url: '/',
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.state('home.modal', {
url: 'modal/:id/',
data: {
modalState: true
},
controller: 'MainCtrl'
});
The child state home.modal should inherit the params of the parent. On your ui-sref from one state to another, pass the param like so:
<a ui-sref="home.modal({ id: x })"></a>
Then your url would turn out to be /modal/:x (where x is the number).
Also the $stateParams should then show the params as you wished.

Resources