Angular : pass parameter to controller from $routeProvider.when - angularjs

I perform a routing upon a broadcast and I need to pass a parameter from $rootScope.$on to a controller through routing.
$rootScope.$on('unauthorized_access', function (event, args) {
$location.path('/login:' + args.msg);
});
Now, this is working.
$routeProvider
.when('/', {
templateUrl: 'mainApp/landingPage/login.html'
})
.when('/login::msg', {
templateUrl: function(params) {
if (params) {
// The params can be printed in console from here...
console.log(params);
}
return 'mainApp/landingPage/login.html';
},
controller: 'loginController'
})
.otherwise({
redirectTo: '/'
});
Now, so far it seems okay. But I need to pass the params to loginController. I tried,
.when('/login::msg', {
templateUrl: function(params) {
if (params) {
console.log(params);
}
return 'mainApp/landingPage/login.html';
},
controller: 'loginController',
pageParams : params
})
but I think, I am doing it wrong. Please help me. Any helps is appreciated. Thank you.
EDIT : I will add a scenario. I need to pass a message-string to loginController from any screen when the 'token' becomes invalid after a fixed time and user is redirected to login screen. Now, when the user arrives for the first time, there is no message to be shown.
Hence, this is how I pass the message to broadcast
$rootScope.$broadcast('unauthorized_access', param);

Use resolve:
$routeProvider
.when("/news", {
templateUrl: "newsView.html",
controller: "newsController",
resolve: {
message: function(messageService){
return messageService.getMessage();
}
}
});
Then in your controller, you can get the data like below:
app.controller("newsController", function (message) {
$scope.message = message;
});
More on this:
http://odetocode.com/blogs/scott/archive/2014/05/20/using-resolve-in-angularjs-routes.aspx

Related

Determine Angularjs Controller based on route params

I have the following configuration for my route:
$routeProvider.when('/', {
controller: 'homeController',
templateUrl: 'app/views/main/home.html'
})
.when('/:section/:tree', {
templateUrl: function($routeParams) { return 'App/Views/'+$routeParams.section+'/'+$routeParams.tree+'.html'; },
controller: function ($routeParams) { return $routeParams.tree + 'Controller'; }
})
.otherwise({
redirectTo: '/'
});
}
The view is loaded correctly but without controller and the controller function is not even called.
Is there any way to solve this issue or determine the controller based on route params.
The controller function is not a function returning the controller name. It's supposed to be the controller itself.
Let's suppose the tree route param can be Foo or Bar, you just need
.when('/:section/Foo', {
templateUrl: function($routeParams) { return 'App/Views/'+$routeParams.section+'/Foo.html'; },
controller: 'FooController'
})
.when('/:section/Bar', {
templateUrl: function($routeParams) { return 'App/Views/'+$routeParams.section+'/Bar.html'; },
controller: 'BarController'
})
This, BTW, will avoid your application to cause an exception is the user enters something other than Foo or Bar in the URL.
If you have many similar routes, just use a loop to define them:
['Foo', 'Bar', 'Baz', 'Brr', ...].forEach(function(tree) {
$routeProvider.when('/:section/' + tree, {
templateUrl: function($routeParams) { return 'App/Views/' + $routeParams.section+'/' + tree + '.html'; },
controller: tree + 'Controller'
});
});

How To Autoload ui-view on nested views?

I have code like this
<a ui-sref="nested.something">something</a>
<div ui-view="nested.something"></div>
how to load ui-view without click ui-sref ?
EXTEND - related to this plunker provided by OP in the comments above
The state definition is:
.state('store', {
views: {
'store': {
templateUrl: 'store.html'
}
}
})
.state('store.detail', {
views: {
'store_detail': {
templateUrl: 'store_detail.html'
}
}
})
Then in this updated plunker we can see that this would do the job
//$urlRouterProvider.otherwise('/store');
$urlRouterProvider.otherwise(function($injector, $location){
var state = $injector.get('$state');
state.go('store.detail');
return $location.path();
});
Reason? states do not have defined url. Which is a bit weird. So, I would honestly rather suggested to do it like this (the link to such plunker):
$urlRouterProvider.otherwise('/store/detail');
//$urlRouterProvider.otherwise(function($injector, $location){
// var state = $injector.get('$state');
// state.go('store.detail');
// return $location.path();
//});
$stateProvider
.state('store', {
url: '/store',
views: {
'store': {
templateUrl: 'store.html'
}
}
})
.state('store.detail', {
url: '/detail',
views: {
'store_detail': {
templateUrl: 'store_detail.html'
}
}
})
There is a working plunker
ORIGINAL
We can use the .otherwise(rule) of $urlRouterProvider, documented here
$urlRouterProvider.otherwise('/parent/child');
As the doc says:
otherwise(rule)
Defines a path that is used when an invalid route is requested.
So, this could be used for some default - start up "redirection"
The .otherwise() could be even a function, like shown here:
How not to change url when show 404 error page with ui-router
which takes '$injector', '$location' and can do even much more magic (on invalid or startup path)
$urlRouterProvider.otherwise(function($injector, $location){
var state = $injector.get('$state');
state.go('404');
return $location.path();
});
ALSO, if we want to fill in some more details into some nested viesw, we can do it by defining multi-named views:
.state('parent.child', {
url: "/child",
views: {
'' : {
templateUrl: 'tpl.child.html',
controller: 'ChildCtrl',
},
'nested.something#parent.child' : {
templateUrl: 'tpl.something.html',
},
}
})
So, if the tpl.child.html will have this anchor/target:
<i>place for nested something:</i>
<div ui-view="nested.something"></div>
it will be filled with the tpl.something.html content
Check it in action here

How to map a segment of a route to routeParams?

Lets say I have two routes defined, served by the same view and controller, such as
/customers/:cutomerId/edit
/customers/add
in a controller I need to determine in which "mode" is view in. How do I map "edit" and "add" segments of the route so that it appears in $routeParams. Is there a way?
You may user route's resolves to solve it.
For example:
myApp.config(function($routeProvider) {
$routeProvider
.when('/customers/:cutomerId/edit', {
templateUrl: 'myView.html',
controller: 'MyController',
resolve: {
mode: function() {
return 'edit';
}
}
})
.when('/customers/add', {
templateUrl: 'myView.html',
controller: 'MyController',
resolve: {
mode: function() {
return 'add';
}
}
});
myApp.controller('MyController', function($scope, mode){
// Now controller knows it's mode.
});

Angular ui-router to accomplish a conditional view

I am asking a similar question to this question: UI Router conditional ui views?, but my situation is a little more complex and I cannot seem to get the provided answer to work.
Basically, I have a url that can be rendered two very different ways, depending on the type of entity that the url points to.
Here is what I am currently trying
$stateProvider
.state('home', {
url : '/{id}',
resolve: {
entity: function($stateParams, RestService) {
return RestService.getEntity($stateParams.id);
}
},
template: 'Home Template <ui-view></ui-view>',
onEnter: function($state, entity) {
if (entity.Type == 'first') {
$state.transitionTo('home.first');
} else {
$state.transitionTo('home.second');
}
}
})
.state('home.first', {
url: '',
templateUrl: 'first.html',
controller: 'FirstController'
})
.state('home.second', {
url: '',
templateUrl: 'second.html',
controller: 'SecondController'
});
I set up a Resolve to fetch the actual entity from a restful service.
Every thing seems to be working until I actually get to the transitionTo based on the type.
The transition seems to work, except the resolve re-fires and the getEntity fails because the id is null.
I've tried to send the id to the transitionTo calls, but then it still tries to do a second resolve, meaning the entity is fetched from the rest service twice.
What seems to be happening is that in the onEnter handler, the state hasn't actually changed yet, so when the transition happens, it thinks it is transitioning to a whole new state rather than to a child state. This is further evidenced because when I remove the entity. from the state name in the transitionTo, it believes the current state is root, rather than home. This also prevents me from using 'go' instead of transitionTo.
Any ideas?
The templateUrl can be a function as well so you check the type and return a different view and define the controller in the view rather than as part of the state configuration. You cannot inject parameters to templateUrl so you might have to use templateProvider.
$stateProvider.state('home', {
templateProvider: ['$stateParams', 'restService' , function ($stateParams, restService) {
restService.getEntity($stateParams.id).then(function(entity) {
if (entity.Type == 'first') {
return '<div ng-include="first.html"></div>;
} else {
return '<div ng-include="second.html"></div>';
}
});
}]
})
You can also do the following :
$stateProvider
.state('home', {
url : '/{id}',
resolve: {
entity: function($stateParams, RestService) {
return RestService.getEntity($stateParams.id);
}
},
template: 'Home Template <ui-view></ui-view>',
onEnter: function($state, entity) {
if (entity.Type == 'first') {
$timeout(function() {
$state.go('home.first');
}, 0);
} else {
$timeout(function() {
$state.go('home.second');
}, 0);
}
}
})
.state('home.first', {
url: '',
templateUrl: 'first.html',
controller: 'FirstController'
})
.state('home.second', {
url: '',
templateUrl: 'second.html',
controller: 'SecondController'
});
I ended up making the home controller a sibling of first and second, rather than a parent, and then had the controller of home do a $state.go to first or second depending on the results of the resolve.
Use verified code for conditional view in ui-route
$stateProvider.state('dashboard.home', {
url: '/dashboard',
controller: 'MainCtrl',
// templateUrl: $rootScope.active_admin_template,
templateProvider: ['$stateParams', '$templateRequest','$rootScope', function ($stateParams, templateRequest,$rootScope) {
var templateUrl ='';
if ($rootScope.current_user.role == 'MANAGER'){
templateUrl ='views/manager_portal/dashboard.html';
}else{
templateUrl ='views/dashboard/home.html';
}
return templateRequest(templateUrl);
}]
});

Automatically redirect to child state

I'm using UI-Router in my AngularJS app and I have routes like this:
angular.module('app.messages').config(function($stateProvider, ACCESSLEVELS) {
$stateProvider.state('messages', {
url: '/messages',
templateUrl: 'messages/list.html',
controller: 'MessagesListCtrl as controller',
resolve: {
'conversations': function(Restangular) {
return Restangular.all('conversations').getList();
}
}
});
$stateProvider.state('messages.detail', {
url: '/{conversationId:[0-9]{1,8}}',
templateUrl: 'messages/conversation.html',
controller: 'MessagesDetailCtrl as controller',
resolve: {
'conversation': function(conversations, $stateParams) {
return conversations.get($stateParams.conversationId);
}
}
});
});
So if you go to /messages it'll show a list of your conversations (and it contains a ui-view for the details view). If you go go /messages/1 you view a single conversation.
When you go to /messages you should automatically be redirected to the most recent conversation, because just showing the list and a message like "Select a conversation from the list" is just stupid :)
The problem is that I don't know how to do this. If I have some redirect logic in the MessagesListCtrl then you'll always have a delay where you see the empty landing page and are then redirected. I can't do it a resolve function since those are also used by the child state. Surely others have had this same problem, how did they solve this?
Make messages an abstract state, keep the resolve, and move the templateUrl and controller to a new state, messages.list. Then, create a parent <ui-view /> for your messages state, and give it a templateUrl with whatever you want the user to see while the message is loading. Should work.
Solved with the help of Nate's answer above:
angular.module('app.messages').config(function($stateProvider) {
$stateProvider.state('messages', {
abstract: true,
templateUrl: 'messages/list.html',
controller: 'MessagesListCtrl as controller',
resolve: {
'conversations': function(Restangular) {
return Restangular.all('conversations').getList();
}
}
});
$stateProvider.state('messages.list', {
url: '/messages',
templateUrl: 'messages/conversation.html',
controller: 'MessagesDetailCtrl as controller',
resolve: {
'conversation': function(conversations) {
var lastId = _.last(_.sortBy(conversations, 'lastwrite')).id;
conversations.selectedId = lastId;
return conversations.get(lastId);
}
}
});
$stateProvider.state('messages.detail', {
url: '/messages/{conversationId:[0-9]{1,8}}',
templateUrl: 'messages/conversation.html',
controller: 'MessagesDetailCtrl as controller',
resolve: {
'conversation': function(conversations) {
conversations.selectedId = $stateParams.conversationId;
return conversations.get($stateParams.conversationId);
}
}
});
});
Basically messages.list and messages.detail show the exact same template and use the same controller. The resolve is just a bit different. I'm keeping track of the selectedId on conversations so I can mark the active conversation in the list.

Resources