ui-router does not work with parameters in route - angularjs

For some reason it does not work as expected for routes with parameters. It does not even render src attribute for such anchors. I made a simple plunk to demonstrate this and here is the code:
<body ng-controller="MainCtrl">
<a ui-sref="categories/foo">category</a>
<a ui-sref="blah">blah</a>
</body>
and routes:
$stateProvider
.state 'blah',
url: 'blah'
.state 'categories',
url: "categories/:name"
First work fine, second - not. Why this happening? What am I doing wrong?

to Navigate to state, with params
ui-sref also takes an state name as function like syntax with object of params(you want to pass to state) as an argument
So replace
<a ui-sref="categories/foo">category</a>
with
<a ui-sref="categories({name:'foo'})">category</a>

you can specify params like this (using the routes you have defined):
<a ui-sref="categories({name:'foo'})">category</a>

Related

AngularJS: What is a modular approach to sidebars and sub-pages?

I'm building an app for fun and practice, and within the app there is a dashboard module. That dashboard module houses other modules. I do this because the dashboard is the parent state for the modules inside of it and I didn't think it would be right to put them in the outside folder with all the other irrelevent modules, because they belong to the dashboard. For example:
/login
/signup
/logout
/profile
/dashboard
/home
/settings
/foo
/bar
...and their states would be dashboard.home with a url of /dashboard/home, and so forth.
In the settings folder, I have a current set up that works, but I don't know if it's considered good practice.
Under the settings directory, I have some partials and controllers. For example:
/login
/signup
/logout
/profile
/dashboard
/home
/settings
/controllers
settingsAccount.controller.js
settingsPassword.controller.js
settingsPersonal.controller.js
/partials
_account.html
_password.html
_personal.html
settings.html
settings.module.js
/foo
/bar
And those partials would be navigated through on the settings.html. How I currently do this is like this..
settings.html:
<div class="row" ng-controller="LeftSidebarController as sidebar">
<div class="col-sm-3">
<div class="page-title">
<h1 view-title>Settings</h1>
</div>
<ul class="list-group">
<li class="list-group-item" ng-class="{'active': sidebar.currentTab(1)}" ng-click="sidebar.setTab(1)">
<a href>Account</a> <i class="fa fa-chevron-right list-group-item__arrow-right"></i>
</li>
<li class="list-group-item" ng-class="{'active': sidebar.currentTab(2)}" ng-click="sidebar.setTab(2)">
<a href>Password</a> <i class="fa fa-chevron-right list-group-item__arrow-right"></i>
</li>
<li class="list-group-item" ng-class="{'active': sidebar.currentTab(3)}" ng-click="sidebar.setTab(3)">
<a href>Personal</a> <i class="fa fa-chevron-right list-group-item__arrow-right"></i>
</li>
</ul>
</div>
<div class="col-sm-7">
<div class="widget-container widget-container--settings fluid-height">
<div ng-if="sidebar.currentTab(1)"
ng-include src="'app/dashboard/settings/partials/_account.html'"
ng-controller="SettingsAccountController as account"></div>
<div ng-if="sidebar.currentTab(2)"
ng-include src="'app/dashboard/settings/partials/_password.html'"
ng-controller="SettingsPasswordController as password"></div>
<div ng-if="sidebar.currentTab(3)"
ng-include src="'app/dashboard/settings/partials/_personal.html'"
ng-controller="SettingsPersonalController as personal"></div>
</div>
</div>
</div>
...and I have a global controller LeftSidebarController, which looks like this:
(function() {
'use strict';
angular
.module('app.controllers')
.controller('LeftSidebarController', LeftSidebarController);
function LeftSidebarController() {
var vm = this;
vm.tab = 1;
vm.setTab = function(tabId) {
vm.tab = tabId;
};
vm.currentTab = function(tabId) {
return vm.tab === tabId;
};
}
})();
So as you can see, I basically just load one of the partials and attach it's controller to it based on which tab is clicked in the sidebar.
Has anyone ever had to do something like this and know a better approach? I'd really appreciate some good guidance, I don't want to make it like this because it seems really messy to me.
I thought having the sub-sub-modules, with a state of something like dashboard.settings.account, with url of /dashboard/settings/account. If I can't find a better approach I'll probably go that route, seems a bit better to me because then everything has it's own state and I will no longer be including multiple files and controllers in a single state. Only problem is I don't know how I'd handle the sidebar yet.
Thanks!
When I build an app using ui-router, I usually structure it this way:
/app
/components
/states
/login
/signup
/logout
/profile
/dashboard
states.module.js
index.html
app.module.js
app.config.js
The components folder is where I put all of my custom directives, services, filters, etc. organized by feature. For example, I might have a notifications sub-folder that contains a service and a directive.
My states folder has a file called states.module.js that would look something like this:
angular.module('app.states', [
'app.states.login',
'app.states.signup',
'app.states.logout',
// etc.
]);
Then each state sub-folder would have a module and all of the files associated with that state. It would also have a {state}.config.js file that adds the state to $stateProvider.
All of that is similar to what you have done (though I wouldn't put controllers and templates in separate folders). The key difference is, in the case of your dashboard state, I would use views.
dashboard.config.js
angular.module('app.states.dashboard')
.config(stateConfig)
;
function stateConfig($stateProvider) {
$stateProvider
.state('dashboard', {
url: '/dashboard/:view',
views: {
'home#dashboard': {
controller : 'DashboardHomeViewController',
controllerAs: 'home',
templateUrl : 'states/dashboard/home.html'
},
'settings#dashboard': {
controller : 'DashboardSettingsViewController',
controllerAs: 'settings',
templateUrl : 'states/dashboard/settings.html'
},
'': {
controller : 'DashboardViewController',
controllerAs: 'dashboard',
templateUrl : 'states/dashboard/dashboard.html'
},
}
)
;
}
I would have files in that folder for each view controller and template, as well as a module file. The main view template file would look something like this:
dashboard.html
<ui-view name="{{dashboard.view}}#dashboard"></ui-view>
Hopefully that answers your question.

AngularJS UI Router creates no href

I am new to AngularJS and i am playing a bit with the routes. But the UI Router does not "convert" my URL to he correct href Attribute.
My View:
<a ui-sref="music.detail({postID: post.id})" class="text-ellipsis">{{post.first_name}}</a>
And this is from my router.js
.state('music.detail', {
url: '/{postId}',
templateUrl: 'tpl/music.detail.html'
})
But the HTML Output is still without the ID. {{post.id}} is working fine and returns the ID of the JSON Object.
<a ui-sref="music.detail({postID: post.id})" class="text-ellipsis ng-binding" href="#/music/">Tremaine Stehr</a>
Am i something missing here?
check your html
<a ui-sref="music.detail({postId: post.id})" class="text-ellipsis ng-binding" >Tremaine Stehr</a>

How to compile template directives with ui-rout and AngularJS

I have the following state.
.state('categories', {
url: '/categories',
templateUrl: 'categories',
controller: 'CategoriesController',
})
And template url loads:
<div>
<a ui-sref="categories.bob">my link text</a>
</div>
But ui-sref isn't compiled. How is the right way to slove this case ?
--[EDIT]--
I mean html loaded form template is not compiled by AnulgarJS. For example
<a ui-sref="categories.bob">my link text</a>
should be
<a ui-sref="categories.bob" href="#/categories/bob">my link text</a>
I assume you want to pass bob as an optional parameter? If that is the case try the following:
Defining a new state:
.state('categories.name', {
url: '/:name',
templateUrl: 'subcategorytemplate',
controller: 'SubCategoryController',
})
The route:
<div>
<a ui-sref="categories.name({name:"bob"})">my link text</a>
</div>
You will find your route parameter in ui-router's $stateParams service by calling $stateParams.name. For further information look here.
In fact i found my problem. My nasted state url's was wrong.
.state('categories.category', {
url: '/:category',
templateUrl: function($params) {
console.log($params);
return 'categories/' + $params.category;
},
})
But I still don't understend how and why <a ui-sref="someUrl"> are rendered like <a ui-sref="someUrl" href="#>someUrl"> buy if I load html with templateUrl no href attribute is generated. So is there a way to make it generate href attribute too ?
How to load my nested view in parent ui-view ?

Updating url on transition from child to parent state in UI router

I have an Angular (1.2.1) app running UI-router (0.2.13), and the following state structure:
$stateProvider.state('home', {
template: "<div home></div>",
url: '/'
}).state('home.geo', {
url:'/geo/{geo}'
}
Transitioning from parent to child or between children with different {geo} parameter values works as expected. Transitioning from child to parent works - i.e. the contents of the template and $state.current change as expected - but the URL does not update in the browser.
To be clear, an example: I'm in /geo/california and I click a button with ui-sref='home'. I've confirmed that the correct href='#/' has been placed on the button, and clicking it causes the $state to transition back to the home state, but /geo/california remains in my address bar.
What am I missing here?
Update in respose to #UlukBiy's comment: No, home does not have a ui-view in its template. The ui-view is in the template of it's parent: The overall structure is:
<body>
<div app-nav></div>
<div ui-view></div>
</body>
So the home directive gets inserted into the ui-view, but it contains no ui-views of its own. Is that my problem? I'm new to UI-router, and assumed there was some low-level misunderstanding about the role of states vs. directives when I posted this. If so, please help me correct it.
This scenario should be working. There is a working example (click the blue button right-top to run example in separate window, showing the address bar)
I updated your state def a bit:
$stateProvider
.state('home', {
url: "/",
template: 'Home view <hr /> Geo view: <div ui-view></div>',
})
.state('home.geo', {
url:'^/geo/{geo}',
templateUrl: 'tpl.html',
})
All these links do work as expected:
<a href="#/home">
<a href="#/geo/california">
<a href="#/geo/czechia">
<a ui-sref="home">
<a ui-sref="home.geo({geo:'california'})">
<a ui-sref="home.geo({geo:'czech'})">
So, the most important change here is that for a child state we should use this url:
url:'^/geo/{geo}',
instead of the url:'/geo/{geo}'. Check the doc:
Absolute Routes (^)
If you want to have absolute url matching, then you need to prefix your url string with a special symbol '^'.
Check the working example here

Angular ui-router reloading ng-include outside ui-view

Probably it's the entire layout wrong but here is my situation, I'm using a Meanjs.org stack with Angular ui-router.
I have a layout like this:
<div data-ng-include="'/modules/core/views/header.client.view.html'"></div>
<div id="page-content" class="clearfix" fit-height>
<div id="wrap" data-ui-view="" class="mainview-animation"></div>
</div>
Now I need to reload the controller inside the header.client.view.html when I change the $state.
For example when I'm in the sign-in page and I login I need to reload the header controller, but having this it's not possible because the ui-router change only the ui-view part with the relative template:
// this change only ui-view, doesn't care about the ng-include before
state('home', {
url: '/',
templateUrl: 'modules/core/views/home.client.view.html',
});
I found the possibility to add more ui-view to the state so I could add a ui-view2 for the header instead using the ng-include but this means having the ui-view2 on each state.
Any suggest?
You might not need to "reload the controller" every time the state changes, instead make your controller react to the state change on the fly and update its properties.
Check out the ui-router $stateChangeSuccess event.
First at all you need to listen #Matti Virkkunen
It's better to listen the state in your header's controller with "$stateChangeSuccess"
So you just have to declare a controller for your header. And inside your header controller add something like this.
$scope.$on('$stateChangeSuccess', function () {
// Do what you want for example check if the current state is home with $state.is('home');
});
Do not forget to declare your controller in your template
<div data-ng-controller="HeaderController" data-ng-include="'/modules/core/views/header.client.view.html'"></div>
<div id="page-content" class="clearfix" fit-height>
<div id="wrap" data-ui-view="" class="mainview-animation"></div>
</div>

Resources