I have states that look like this:
$stateProvider.state('base', {
url: '/base',
redirectTo: 'base.all',
template: '<div ui-view></div>'
})
.state('base.all', {
parent: 'base',
url: '?someQuery&another',
params: {
someQuery: null,
another: null
},
views: {
'': {
template: '<div>base.all</div>',
controller: function($stateParams){
console.log($stateParams.someQuery);//undefined
console.log($stateParams.another);//undefined
}
}
//some views
}
})
.state('base.all2', {
parent: 'base',
url: '/subPath?someQuery&another',
params: {
someQuery: null,
another: null
},
views: {
'': {
template: '<div>base.all2</div>',
controller: function($stateParams){
console.log($stateParams.someQuery);//foo
console.log($stateParams.another);//bar
}
}
//some views
}
});
I listen to the event $stateChangeStart and do $state.go when redirectTo is present.
My problem now is if I go to the url /base?someQuery=foo&another=bar
It will correctly go to the base.all-state but the $stateParams of the base.all-state will be empty. I don't want this state to have a subPath. The child state will only get the urlParameters set to $stateParams if it has its own subPath like /base/subPath?someQuery=foo&another=bar then it works fine and everything is set to $stateParams.
Is my attempt to have a subState with no subUrl, but only the queryParameters, possible? If so how?
It was as simple to not use the redirectTo and set parent state to abstract.
I think I was too blinded with the use of redirectTo.
And I'm not getting any warnings when routing /base, it seems it will automatically find the state base.all and everything works as expected.
Related
How can keep url parameters for state, after page refresh?
I have this state
.state('App.State', {
abstract: true,
url: '/App/:param',
templateUrl: '/page.html',
controller: 'pageCtrl',
params: {
'param': null,
},
})
I go to this state with this command
<a ui-sref="App.State({param: paramName})">{{paramName}}</a>
I go to the state, with the params that i want, but when i refresh page (f5) i loose the params, how can keep them after page refresh?
There is a plunker which does remove abstract: true from your code:
.state('App', {
template: '<div ui-view ></div>',
})
.state('App.State', {
//abstract: true,
url: '/App/:param',
templateUrl: 'page.html',
controller: 'pageCtrl',
params: {
'param': null,
},
})
And then we can use any kind of url (even F5 later) to get to that state
http://run.plnkr.co/WFREZ72rUUbD4ekc/#/App/SomeParam
http://run.plnkr.co/WFREZ72rUUbD4ekc/#/App/OtherParam
In case, we want to keep that state abstract - we cannot navigate to it. We need its child. There is other plunker
.state('App', {
template: '<div ui-view ></div>',
})
.state('App.State', {
//abstract: true,
url: '/App/:param',
templateUrl: 'page.html',
controller: 'pageCtrl',
params: {
'param': null,
},
})
.state('App.State.Child', {
url: "/",
template: '<h4> App.State.Child </h4>',
})
The ui-sref cannot be
<a ui-sref="App.State({param: 'xxx'})">
-- not working while abstract
we have to go to child
<a ui-sref="App.State.Child({param: 'yyy'})">
And these will work then
http://run.plnkr.co/217SWcbOjuv1k3YF/#/App/MyParam/
http://run.plnkr.co/217SWcbOjuv1k3YF/#/App/MyOtherParam/
To maintain the values you should add the parameters in the url like this:
url: '/App/:param'
And remove the params property.
I'm developing an AngularJs application using UI-Router(-extras) in which I use the following setup:
.state('root', {
url: '/{language:(?:nl|en)}',
views: {
'root': {
templateUrl: 'app/root/root.html',
controller: 'RootController'
}
}
})
.state('root.main', {
views: {
'main': {
templateUrl: 'app/main/main.html',
controller: 'MainController'
}
},
sticky: true
})
.state('root.modal', {
url: '/{locale:(?:nl|fr)}',
views: {
'modal': {
templateUrl: 'app/modal/modal.html',
controller: 'ModalController'
}
}
})
The root state defines the language in the URL. Furthermore I have several modal and main states which have their own URL (i.e. root.main.home => /en/home).
Now I want to have some modal states that have no URL. How can I make a state ignore his parent's URL?
To answer:
how can a state ignore his parent's URL?
we have
Absolute Routes (^)
If you want to have absolute url matching, then you need to prefix your url string with a special symbol '^'.
$stateProvider
.state('contacts', {
url: '/contacts',
...
})
.state('contacts.list', {
url: '^/list',
...
});
So the routes would become:
'contacts' state matches "/contacts"
'contacts.list' state matches "/list". The urls were not combined because ^ was used.
Also check this: how to implement custom routes in angular.js?
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.
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);
}]
});
Here is my ui-router configuration:
$stateProvider
.state('search.filter.results', {
url: '/results?profileId&keywords',
views: {
'profiles#': {
templateUrl: 'profile-list/profile-list.html',
controller: 'ProfileListCtrl'
},
'profile-summary#search.filter.results': {
templateUrl: 'profile-list/profile-summary/profile-summary.html'
},
'profile-details#search.filter.results': {
templateUrl: 'profile-list/profile-details/profile-details.html',
controller: 'ProfileDetailsCtrl'
}
}
})
ProfileListCtrl has the following method:
$scope.showProfileDetails = function(profileId) {
$state.go('search.filter.results', { profileId: profileId });
};
The problem is, when this method is executed, ProfileListCtrl is instantiated and the view is reloaded.
I would like the reload to happen only if the keywords query parameter changes, but not when the profileId parameter changes.
What would be the best way to achieve this?
I would suggest: split this state into 2 states.
The "parent" keeping the profileId,
the "child" working with keywords.
Until the profileId is not changed, the parent controller won't be re-instantiated.
I created this example with this simplified states definition:
.state('profile', {
url: '/results/:profileId',
views: {
'profiles#': {
//templateUrl: 'profile-list/profile-list.html',
template: '<div>'
...
'<div ui-view=""></div></div>',
controller: 'ProfileCtrl'
},
}
})
.state('profile.results', {
url: '/?keywords',
views: {
'': {
//templateUrl: 'profile-list/profile-list.html',
template: ...
controller: 'ProfileListCtrl'
},
}
})
NOTE: state defintion here is simplified, to just show the split. Not all the details. Also, the child state target parents unnamed view ui-view="", because these should be nested. Finally, parent could even be abstract
This approach also will require some small adjustments of the url. It is not working well if the child url defintion would start with ? like: url: '?keywords'. So I used different pattern:
/results/profileId-value/?kewords=abc