I am using UI-Router in my angular Meteor project.
When I am in route /search and want to relocate to /search/:term (for example /search/starwars) and I use in my controller
$state.go( 'private.search.feed', { term: 'starwars' } )
then it URL in my browser turns in to /search/search/starwars instead of /search/starwars.
My route setup (I have 2 layouts, hence the abstract views)
$stateProvider
# abstract views / layout
.state 'private',
url: ''
abstract: true
views:
'app':
templateUrl: 'client/views/layouts/privateLayout.html'
.state 'public',
url: ''
abstract: true
views:
'app':
templateUrl: 'client/views/layouts/publicLayout.html'
# end abstract views / layout
.state 'private.home',
url: '/'
views:
"container#private":
templateUrl: 'client/views/home/home.html'
.state 'private.search',
url: '/search'
views:
"container#private":
templateUrl: 'client/views/search/search.html'
controller: 'searchCtrl'
.state 'private.search.feed',
url: '/search/:term'
views:
"container#private":
templateUrl: 'client/views/search/feed/feed.html'
controller: 'searchCtrl'
When I change the route to
.state 'private.searchFeed',
url: '/search/:term'
views:
"container#private":
templateUrl: 'client/views/search/feed/feed.html'
controller: 'searchCtrl'
and call
$state.go( 'private.searchFeed', { term: 'starwars' } )
the url is correct.
I reckon I defined a child state in the first setup, but I don't understand why it would repeat search in the URL.
Any thoughts?
You private.search.feed state url should be '/:term' because the URL of parent state gets inherited in state derived from it. As your derived state has to be like /:term will apparently become/accessible via /search/:term
Code
.state 'private.search.feed',
url: '/:term'
views:
"container#private":
templateUrl: 'client/views/search/feed/feed.html'
controller: 'searchCtrl'
Using a dot in the name of a state indicates to ui-router that it is a child state of whatever state is defined by the dotpath. For example:
State 1: home (/home)
State 2: home.dashboard (/home/dashboard)
State 3: home.dashboard.settings (/home/dashboard/settings)
You cannot create a new state State 4 called home.dashboard.settings.save whose route does not begin with the URL given to State 3, as State 4's dotpath indicates that it should inherit from State 3.
So you need to ensure that a URL for a child state does not repeat the URL of its parent:
.state 'private.search.feed',
url: '/:term'
Related
I am passing a value from one state to another using UI-Router. In my controller when the url is updated I am trying to get access to the second parameter of my url using $stateParams, but for some reason I can get access to the first parameter but the second one is UNDEFINED. This is my code:
state 1, URL:
http://localhost:16009/#/5nqeAPqlv21/
state 2, URL:
http://localhost:16009/#/5nqeAPqlv21/details/PP163Ku3dOR
candidateContoller.js:
//go to state 2 (same controller for parent and child views)
$state.go('index.details', { "submissionIdd": publicSubmissionId });
//when located in the new state and new url:
console.log($stateParams.submissionIdd); //shows undefined
console.log($stateParams.token); //shows 5nqeAPqlv21
App.js:
$stateProvider
.state('index',
{
url: '/:token/',
views: {
'': {
templateUrl: 'AngularJS/Templates/indexView.html',
controller: 'candidateController as candCtrl'
},
'sectioncandidate#index': {
templateUrl: (_isNotMobile)
? 'AngularJS/Templates/candidatesView.html'
: 'AngularJS/Templates/candidatesMobileView.html'
}
}
})
.state('index.details', {
url: 'details/{submissionIdd}',
views: {
'sectioncandidate#index': {
templateUrl: (_isNotMobile)
? 'AngularJS/Templates/candidateView.html'
: 'AngularJS/Templates/candidateMobileView.html'
}
}
})
You do experience standard behavior of the UI-Router and Parent/Child state defintion:
parent state declares $stateParams (as url: '/:token/') - so there is just one declared - a token
child state gets all from parent(s), plus declares new parameter (as url: 'details/{submissionIdd}') - so it has both defined - token and submissionIdd,
So, while child has access to both params, parent state has just a token parameter
state 1 === PARENT, URL:
http://localhost:16009/#/5nqeAPqlv21/
here we will have submissionIdd undefined
state 2 === CHILD, URL:
http://localhost:16009/#/5nqeAPqlv21/details/PP163Ku3dOR
both are there submissionIdd=PP163Ku3dOR and token=5nqeAPqlv21
Extend - there is a working plunker
This states definition (in the plunker a bit adjusted)
.state('index',
{
url: '/:token/',
views: {
'': {
templateUrl: 'indexView.html',
controller: 'candidateController as candCtrl'
},
'sectioncandidate#index': {
templateUrl: 'parent.html'
}
}
})
.state('index.details', {
url: 'details/{submissionIdd}',
views: {
'sectioncandidate#index': {
templateUrl:'child.html',
}
})
will properly show state params for these links
href
<a href="#/5nqeAPqlv21/">
<a href="#/5nqeAPqlv21/details/PP163Ku3dOR">
<a href="#/5nqeAPqlv21/details/other">
ui-sref
<a ui-sref="index({token: '5nqeAPqlv21'})">
<a ui-sref="index.details({token: '5nqeAPqlv21', submissionIdd: 'another'})">
Check it here
You can prefer not to pass the the params in the URL unless there is an explicit requirement. I would prefer to use $stateParams in controller to fetch the data.
For this you need to define the default params in the state to
make it work.
$stateProvider .state('index.details', {
url: 'details',
params: {
submissionIdd: 'default',
},
views: {
'sectioncandidate#index': {
templateUrl: (_isNotMobile) ? 'AngularJS/Templates/candidateView.html' : 'AngularJS/Templates/candidateMobileView.html' ,
controller: function($stateParams){
console.log($stateParams.submissionIdd)
}
}
}
})
This should be simple but I just can't figure it out.
I have two routes:
$stateProvider
.state('users', {
url: '/users',
views: {'#': {
templateUrl: 'templates/users.tpl.html',
controller: 'UsersCtrl as users'
}}
})
.state('users.edit', {
url: '/{userId}',
views: {'#': {
templateUrl: 'templates/editUser.tpl.html',
controller: 'EditUserCtrl as editUser'
}}
});
This works fine if you use a ui-sref or $state.go to navigate to the "users" state by name, but if you type domain.com/users/ into the URL bar it goes to the edit state with empty userId parameter even though there is nothing after the trailing stash.
Normally this wouldn't be a problem but firing the editUser route with no userId causes console errors which would be a pain to fix.
Is this easily fixable?
Take a look to this post:
https://github.com/angular-ui/ui-router/issues/50
and this link referenced in the post:
https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-make-a-trailing-slash-optional-for-all-routes
Your states definition should be
$stateProvider
.state('users', {
url: '/users',
views: {'#': {
templateUrl: 'templates/users.tpl.html',
controller: 'UsersCtrl as users'
}}
})
.state('users.edit', {
url: '/edit/{userId}',
views: {'#': {
templateUrl: 'templates/editUser.tpl.html',
controller: 'EditUserCtrl as editUser'
}}
});
your child state url should be '/edit/{userId}'. When we create a child state its url automatically added its parent state url. So if your url is '/{userId}' it will be 'domain.com/users/{userId}'. That's why for domain.com/users/ its navigate to the state users.edit with null parameter value.
https://github.com/angular-ui/ui-router/wiki/nested-states-%26-nested-views
My question is simple, but I couldn't find solution anywhere else.
For example I have a normal route state with parameter,
.state('page', {
url: '/page/:pageid',
templateUrl: 'templates/pages.html',
controller: 'pagesCtrl'
})
However for example, if I have 1000 pages, but for the Number 1 and Number 999 pages I have to use another template. How could I simply do this? Something like
.state('page', {
url: '/page/:pageid',
templateUrl: 'templates/pages.html',
controller: 'pagesCtrl'
})
.state('page', {
url: '/page/1',
templateUrl: 'templates/page1.html',
controller: 'pagesCtrl'
})
.state('page', {
url: '/page/999',
templateUrl: 'templates/page999.html',
controller: 'pagesCtrl'
})
Will this work?I tested, the later 2 options are not overriding the original state with parameter.
If I wish to use the same controller, how to load the 1 and the 999 as the pageid parameter in the controller?
Definitely you shouldn't have two more state for separate template. You should use single generic state which will take templateUrl with the help of passed state parameter.
Code
.state('page', {
url: '/page/:id',
templateUrl: function($stateParams){
var template = $stateParams.id.indexOf([1, 1000]) > -1?'pages.html'
:'page'+$stateParams.id+'.html';
return 'templates/'+template ,
}
controller: 'pagesCtrl'
})
I am using Ionic and Angular 1 (with ui-router). Let's say I have 2 tabs:
+----+ +-------+
|HOME| |PROFILE|
+----+ +-------+
This is the important part: there's a link within the HOME tab to /profile... but, I do not want it to automatically switch to the PROFILE tab when I click that link... I want to continue with the navigation stack within the HOME tab.
The usual approach using ui-router to have multiple nested states is something like this (some unnecessary things for this example purposefully omitted):
$stateProvider
.state('app', {
abstract: true,
template: require('templates/app.html'),
})
.state('app.home', {
template: require('templates/home.html'),
url: '/home',
controller: 'HomeCtrl',
views: {
// a bunch of child views that can only belong to this state
}
})
.state('app.profile', {
template: require('templates/profile.html'),
url: '/profile',
controller: 'ProfileCtrl',
views: {
// a bunch of child views that can only belong to this state
}
});
But what if I want to navigate to app.profile inside of app.home, instead of being forced to change tabs?
One options is to make all my tabs abstract and then declare <tab>.<state> for every possible state (using a loop), but this seems like it requires a lot of possibly unnecessary overhead:
var abstractStates = [
{
name: 'home',
url: '/home',
controller: 'HomeCtrl',
template: require('templates/home.html')
},
{
name: 'profile',
url: '/profile',
controller: 'ProfileCtrl',
template: require('templates/profile.html')
}
];
abstractStates.forEach(function(state) {
$stateProvider.state(state.name, _.omit(state, ['name']);
abstractStates.forEach(function(nestedState) {
$stateProvider.state([state.name, nestedState.name].join('.'), _.omit(nestedStates, ['abstract', 'name']));
});
});
Is there a better way to accomplish this?
I'm using UI-Router module for routing. I have 2 states that router should match the urls with them according to nested routes laws :
// Dashboard
.state('dashboard', {
url: "/dashboard",
templateUrl: "dashboard/views/index.html",
controller: "DashboardController",
...
})
// Users
.state('users', {
url: "/users",
templateUrl: "users/views/index.html",
controller: "UsersController",
...
})
// Single User
.state('users.id', {
url: "/{id:(?:[a-z0-9_-]{3,16})}",
templateUrl: "users/views/show.html",
controller: "UserController",
...
})
also I have set a default route :
$urlRouterProvider.otherwise("/dashboard");
Problem :
when I go to http://127.0.0.1:8000/app/#/users/testuser123, it shows index.html from users state instead of show.html from users.id state. What's the Problem ?
You should add users within your url definition for users.id if you call http://127.0.0.1:8000/app/#/users/testuser123
.state('users.id', {
url: "/users/{id:(?:[a-z0-9_-]{3,16})}",
templateUrl: "users/views/show.html",
controller: "UserController",
...
})