Angular UI Router - Dynamic states get double slashes when navigating with ui-sref
I am building a CMS system so I want the states to be dynamically generated. Since you cannot create HTTP requests in the configuration phase, I decided to add my routes to the .run () function as described here: http://blog.brunoscopelliti.com/how-to-defer-route-definition-in- an-angularjs-web-app
Now when I display a list of links in my partial navigation using the ui-sref directive, the url being added has two forward slashes instead of one. The controller and everything is named accordingly, but I can't seem to get rid of the double slashes. Any ideas?
Here's the relevant code:
app.js
var $stateProviderReference;
angular.module('app', [
'ui.router', // Angular UI Routing
'ngStorage', // Angular Storage (local, cookies, session)
'ngAnimate', // Angular Animation
'angular-loading-bar', // Angular Loading Bar
'restangular', // Restangular
'angular.assets.injector', // Custom assets injector
])
.config(function($stateProvider, $urlRouterProvider, RestangularProvider, $httpProvider, cfpLoadingBarProvider){
// Save a reference of our stateProvider
$stateProviderReference = $stateProvider;
// Irrelevant configuration
// States (routes)
$stateProvider
/*
|
| Frontend
|
*/
.state('frontend', {
url: '/',
views: {
'': {
templateUrl: 'app/views/frontend/templates/default/bootstrap.html',
controller: 'FrontendCtrl'
},
'nav@frontend': {
templateUrl: 'app/views/frontend/templates/default/partials/navigation.html',
controller: 'NavCtrl',
resolve: {
pages: function(PageService){
return PageService.all();
}
}
},
'footer@frontend': {
templateUrl: 'app/views/frontend/templates/default/partials/footer.html',
controller: 'FooterCtrl'
},
'page@frontend': {
templateUrl: 'app/views/frontend/pages/page.html',
controller: 'PageCtrl'
}
}
});
.run(function($rootScope, $location, $state, $stateParams, AuthService, CSRF_TOKEN, PageService){
// Retrieve all the pages
PageService.all().then(function(pages){
// Loop through all the pages
angular.forEach(pages, function(page){
// If this is not the homepage
if( page.routeName !== 'frontend' )
{
// Add a state to our stateProvider
$stateProviderReference.state(page.routeName, {
// Set the desired url
url: page.url,
// Views that we are populating
views: {
// Override the page view partial
'page@frontend': {
templateUrl: 'app/views/frontend/pages/page.html',
controller: 'PageCtrl'
}
}
});
}
});
});
navigation.html partial
<ul class="nav navbar-nav">
<li ng-repeat="page in pages">
<a ng-class="{'active': isActive({{page.routeName}})}" ui-sref="{{ page.routeName }}">{{ page.title }}</a>
</li>
</ul>
The page data retrieved from the db is formatted like this:
{
id: 1,
routeName 'frontend.home',
title: 'Homepage',
url: '/home',
metaData: {
tags: "some random tags",
desc: "some random description"
},
content: "Html goes here",
deletable: 'yes' // or 'no'
}
So everything works, except for the double slashes. When I click on the link generated for the above sample code, it will send me the following URL:
http://localhost/CMSv2/public/#//home
instead
http://localhost/CMSv2/public/#/home
I tried removing the slash from the routeName property and then adding no double slashes, but the controller can no longer be reached and everything seems to break.
source to share
The problem comes from the fact that if there are parent elements parent, then their url is parent.url + child.url:
- parent:
.state('frontend', { url: '/',...
- child:
{ routeName 'frontend.home', url: '/home',..
- result:
'/' + '/home' === '//home'
But again there is a good solution built in ui-router
:
small cite:
If you want to have an absolute URL match, you need to attach the url string with the special character "^".
$stateProvider
.state('contacts', {
url: '/contacts',
...
})
.state('contacts.list', {
url: '^/list',
...
});
SOLUTION: (very simple in this case)
Instead url def:
{
id: 1,
routeName 'frontend.home',
title: 'Homepage',
url: '/home',
...
We'll just send this one : url: '^/home',
{
id: 1,
routeName 'frontend.home',
title: 'Homepage',
url: '^/home',
...
source to share
Since your home state comes from frontend state, an extra forward slash is added from the frontend. To remove the extra leading slash, set the frontend-state to "abstract" and clear the url like this:
.state('frontend', {
abstract: true,
url: '',
templateUrl: 'yourTemplate.html'
})
This fits your structure better since the frontend url is never used.
source to share