I have implemented UI-Router in my application but I'm having a problem with a nested route. My index.html page looks like;
<body>
<!-- Navigation code -->
<!-- Content from the template -->
<div ui-view></div>
<!-- Footer -->
<!-- Application Scripts -->
</body>
My Angular ui-route code is;
(function () {
'use strict';
var app = angular.module('rbApp', ['ngAnimate', 'ui.router', 'ui.bootstrap']);
app.config(["$stateProvider", "$urlRouterProvider", function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/");
$stateProvider
.state("home", {
url: "/",
templateUrl: "/App/WebSite/home.html",
controller: "homeController as homeVm"
})
.state("programs", {
url: "/programs",
templateUrl: "/App/WebSite/programs.html",
controller: "programsController as programVm"
})
.state("programs.complete", {
url: "/complete",
templateUrl: "/App/WebSite/programsComplete.html"
})
.state("articles", {
url: "/articles",
templateUrl: "/App/WebSite/articles.html",
controller: "articleController as articleVm"
})
}]);
app.run(['$rootScope', function ($rootScope) {
$rootScope.$on('$locationChangeStart', function (event, newUrl, oldUrl) {
console.log('Location change: %s --> %s', oldUrl, newUrl);
});
}]);
})();
When I select the "programs" route the page is displayed correctly and the "app.run" code above updates the console log with;
Location change: http://localhost:50321/#/ --> http://localhost:50321/#/programs
On the programs page I have an image within an anchor tag as follows;
<div class="col-md-3 hidden-sm hidden-xs">
<a ui-sref="programs.complete"><img class="img-thumbnail img-responsive" src="/Images/Programs/Program01Small.jpg" /></a>
</div>
When the image is clicked the console log displays;
Location change: http://localhost:50321/#/programs --> http://localhost:50321/#/programs/complete
So it appears the routes are behaving as expected. The only problem is the "programsComplete.html" template is not displayed, the browser continues to display the "programs.html" template.
I have checked the console log but no errors are displayed and currently the "programsComplete.html" only contains a <h1> tag and text so I can confirm the template is being loaded.
Can anyone advise why this would not be loading the template?
It seems that the parent template programs.html is just missing for its child. Be sure that this tempalte programs.html contains unnamed view target
<div ui-view="">
Each child is inserted into its parent. In case we want child to replace parent, we have to use absolute name, and in this case target the index.html like this:
.state("programs.complete", {
url: "/complete",
views : {
'#' : {
templateUrl: "/App/WebSite/programsComplete.html"
}
}
})
while this naming convention could be weird: views : { '#' : ..., it is just absolute name:
View Names - Relative vs. Absolute Names
Behind the scenes, every view gets assigned an absolute name that follows a scheme of viewname#statename, where viewname is the name used in the view directive and state name is the state's absolute name, e.g. contact.item. You can also choose to write your view names in the absolute syntax.
so '#' means unnamed view in the unnamed state === root state
Related
Here is the code which i have written so as to change my view (without any change in the url)
<a ui-sref="chat.menu" ng-click="click1();">Latest</a>
<a ui-sref="chat.menu" ng-click="click2();">Mail</a>
<div ui-view="{{item}}"></div>
var app = angular.module('myApp',['ui.router'])
.run(['$rootScope','$state','$stateParams', function($rootScope,$state,$stateParams){
$rootScope.$state = $state;
$state.$stateParams = $stateParams;
}]);
app.config(function($stateProvider,$urlRouterProvider){
$urlRouterProvider.otherwise('/');
$stateProvider
.state('home',{
url:'/',
templateUrl:'templates/main.html',
})
.state('chat',{
url:'/chat',
templateUrl:'templates/chatPage.html',
controller: 'chatController',
})
.state('chat.menu',{
views: {
'':{
template: '<h1>Blank</h1>',
},
'latest': {
template: '<h1>Latest</h1>',
},
'mail': {
template: '<h1>Mail</h1>',
}
}
});
});
app.controller('Controller', function($scope,$state, authentication){
$scope.item='';
$scope.click1 = function(){
$scope.item = "latest";
}
$scope.click2 = function(){
$scope.item = "mail";
}
});
Its working fine, but the problem is that I change the the view only once. Once a view is loaded in the ui-view, then another view is not getting loaded in it on the click of the button.
All i want is that, when the user click the button the view should be changed according to the click of the button without any change in the url.
So I'd trying something like this: having two states instead of one. If you want to keep only one state, you'd need to pass parameters to that state, but for a simple example like this, two states will work just fine:
myApp.config(function ($stateProvider){
$stateProvider
.state("latest", {
url: "#",
template: "<h1>Latest</h1>",
controller: "Ctrl1"
})
.state("mail", {
url: "#",
template: "<h1>Mail</h1>",
controller: "Ctrl2"
});
});
What we are doing here is either setting the URL to the same in both states, or setting no URL at all. Also, if you want to assign controllers to your views, here is were we do it. You can assign no controllers, two different controllers, or the same controller if you would like.
Then setup your view like this:
<nav>
<a ui-sref="latest">Latest</a>
<a ui-sref="mail">Mail</a>
</nav>
<div ui-view></div>
Here is a working JSFiddle
I'm trying to use ui-route do control my app states but I want the states to be URL oriented.
I want to click on a link and automagically goes to the state with the associated URL.
I followed the In-Depth Guide and my code looks like this (please see plnkr https://plnkr.co/edit/wk1RphKq6G3t4GqfppYm):
JS:
angularModule.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
//$urlRouterProvider.otherwise("/management");
$stateProvider
.state('management', {
url: '/',
template: '<p>:)</p>'
})
.state('management.users', {
url: '/users',
templateUrl: './users.html'
});
}]);
HTML:
<div class="container-fluid" ng-controller="PageController">
<aside class="sidebar col-md-3">
<nav>
<ul>
<li>Management</li>
<li>Users</li>
</ul>
</nav>
</aside>
<section class="content col-md-9" ui-view>
</section>
</div>
What am I missing?
ui-router has it's own html properties , so instead of
href="management/users"
you should use
ui-sref="management.users"
the path name you use with ui-sref is the state name not the url path itself.
take a look at this https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-sref
hope this helps
When you define the states, set the root state url to '/':
$stateProvider
.state('management', {
url: '/', // INSTEAD OF "index.html"
template: '<p>:)</p>'
})
.state('management.users', {
url: '/users',
templateUrl: 'users.html'
});
Next. You're trying to inject 'dev.api' into the root module:
angularModule = angular.module('app', [
'ui.router',
'ui.bootstrap',
'dev.api' // HERE
]);
BUT you never creating the module. You need to change the service.js to this:
angular
.module('dev.api', []); // THIS IS HOW YOU DEFINE A NEW MODULE (Without external dependencies)
angular
.module('dev.api').service('usersApiService', ['$http', function ($http) { .....
Now you'll see that the ui-sref are working (https://plnkr.co/edit/XbDx2EonQvZYXsyvWhUW?p=preview). Note that you still have some JS errors, but those are related to the logic of your app, and not relevant the the question.
I am using ui-router and I try to create several views inside one of my nested states.
In my logic, all of the views should be visible whenever the parent state is active.
I have read the wiki page on multiple named views multiple times, but could not get anything working yet.
My state template would show up, but my views never do.
Here is a working plunker
(You have to click on "Followers" in the navbar for the view to show up. Haven't figured why yet).
Important parts are the config
app.config([
'$stateProvider',
'$urlRouterProvider',
function ($stateProvider, $urlRouterProvider){
$stateProvider
.state('dashboard', {
url: '/dashboard',
templateUrl: 'dashboard.html',
controller: 'DashboardCtrl',
authenticate: true
}).state('dashboard.followers', {
url: '/followers',
templateUrl: 'dashboard.followers.html',
controller: 'DFollowersCtrl',
authenticate: true
}).state('dashboard.followers.add', {
views: {
'add': {
templateUrl: 'dashboard.followers.add.html',
controller: 'DFollowersAddCtrl',
authenticate: true
}
},
});
$urlRouterProvider.otherwise('dashboard');
}
]);
The main dashboard template (level 2, using a generic ui-view)
<div class="col-sm-9 col-sm-offset-3 col-md-10 col-md-offset-2 main">
<div class="flash-area">
<flash-message duration="2000" show-close="true"></flash-message>
</div>
<ui-view></ui-view>
</div>
and the dashobard.followers specific level 3 view, that has a specific name
<div>
<h1 class="page-header">Followers</h1>
<div ui-view="add"></div>
</div>
The trick is coming from, I think, a combination of :
I want to use level 3 nesting
Level 2 uses a generic ui-view, because it may contains my dashboard or something else.
Level 3 contains specific views, as it is where I want to use "views" and not "states" (as far as I understood, at least).
The final aim is to have more than one view in my template, but for now I reduced the attempts to only show the 'add' view.
I have seen several similar questions on SO already, such as this one or this other one but so far my attempts have not been fruitful.
I can access my "add" view directly if I reach its URL (when I try setting one)
But the dashboard.followers state does not get populated by the views.
I think there are several mistakes here you made:
if you want the url of dashboard.followers state and dashboard.followers.add state to be same, the child state dashboard.followers.add does not need the url option
probably can be a mistake(I am not sure because no code is provided), if you don't use the
views: { ... }
named views, but just directly use
url: '/followers',
templateUrl: '/partials/dashboard.followers.html'
angular just assume you want to insert the template in an unnamed <div ui-view></div> in the parents state's template not the root template. for example, in my example code, for state dashboard.followers, since it is a child state of dashboard, if I want to insert the template in root html template, I have to use
views: {
'#': {
template: '<div><h1 class="page-header">Followers</h1><a ui-sref="dashboard.followers.add">add</a><div ui-view="add"></div></div>'
}
}
/* myApp module */
var myApp = angular.module('myApp', ['ui.router'])
.config(['$stateProvider', function($stateProvider) {
$stateProvider
.state('landing', {
url: '/',
template: '<p>landing</p><a ui-sref="dashboard">dashboard</a>'
})
.state('dashboard', {
url: '/dashboard',
template: '<p>landing</p><a ui-sref="dashboard.followers">followers</a>'
})
.state('dashboard.followers', {
url: '/followers',
views: {
'#': {
template: '<div><h1 class="page-header">Followers</h1><a ui-sref="dashboard.followers.add">add</a><div ui-view="add"></div></div>'
}
}
})
.state('dashboard.followers.add', {
views: {
'add': {
template: '<p>followers</p>'
}
}
});
}])
.controller('MyAppCtrl', function($scope, $state /*, $stateParams*/ ) {
$state.go("landing");
});
<body ng-app="myApp" ng-controller="MyAppCtrl">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.js"></script>
<div ui-view></div>
</body>
update
I made two plunkers to fit two different situation:
if you want to dynamically load add state to ui-view="add" by a
link, check
this out.
if you just want a sub template to be loaded always on dashboard.followers state, simply remove add states, and use views: {...} to load the add template in. here is the plunker.
Building a multi-step form (“wizard”). Was originally following this tutorial, which worked great, but am now trying to adapt it so step one is embedded on the homepage rather than being a separate state. No matter what I try, I can not create a ui-sref path that will work. I always get:
Could not resolve '.where' from state 'home'
or
Could not resolve 'wizard.where' from state 'home'
or
Could not resolve 'wizard.where#' from state 'home'
…even though wizard.where# works fine in <div ui-view="wizard.where#"></div>. What is the correct syntax?
Here are the relevant files:
home.js (left comments intact so you can see various methods I’m trying):
var wizard = {
url: '/home/wizard',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard.tpl.html'
};
angular.module( 'myApp.home', [
'ui.router',
'ui.bootstrap',
'myApp.modal',
'angularMoment'
])
.config(function config( $stateProvider, $urlRouterProvider ) {
$stateProvider
.state( 'home', {
url: '/home',
views: {
"main": {
controller: 'HomeCtrl',
templateUrl: 'home/home.tpl.html'
},
"jumbotron": {
controller: 'HomeCtrl',
templateUrl: 'home/welcome.tpl.html'
},
"wizard": wizard,
"wizard.where": {
url: '/home/wizard/where',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard-where.tpl.html',
parent: wizard
},
"wizard.what": {
url: '/home/wizard/what',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard-what.tpl.html',
parent: wizard
},
"wizard.when": {
url: '/home/wizard/when',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard-when.tpl.html',
parent: wizard
},
},
data: { pageTitle: 'Home' }
})
// route to show our basic form (/wizard)
// .state('wizard', {
// url: '/wizard',
// views: {
// "main": {
// controller: 'VendorsCtrl',
// templateUrl: 'vendors/wizard.tpl.html'
// }
// },
// abstract: true,
// //data: { pageTitle: 'Vendor Search' }
// })
// nested states
// each of these sections will have their own view
// url will be nested (/wizard/where)
// .state('wizard.where', {
// url: '/where',
// templateUrl: 'vendors/wizard-where.tpl.html'
// })
// url will be /wizard/when
// .state('wizard.when', {
// url: '/when',
// templateUrl: 'vendors/wizard-when.tpl.html'
// })
// url will be /wizard/vendor-types
// .state('wizard.what', {
// url: '/what',
// templateUrl: 'vendors/wizard-what.tpl.html'
// })
;
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/home/wizard/where');
})
wizard.tpl.html:
<div class="jumbotron vendate-wizard" ng-controller="VendorsCtrl as vendorsCtrl">
<header class="page-title">
<h1>{{ pageTitle }}</h1>
<p>Answer the following three questions to search available vendors. All answers can be changed later.</p>
<!-- the links to our nested states using relative paths -->
<!-- add the active class if the state matches our ui-sref -->
<div id="status-buttons" class="text-center">
<a ui-sref-active="active" ui-sref="wizard.where#"><span>1</span> Where</a>
<a ui-sref-active="active" ui-sref="wizard.what#"><span>2</span> What</a>
<a ui-sref-active="active" ui-sref="wizard.when#"><span>3</span> When</a>
</div>
</header>
<!-- use ng-submit to catch the form submission and use our Angular function -->
<form id="signup-form" ng-submit="processForm()">
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view="wizard.where#"></div>
</form>
</div>
wizard.where.tpl.html:
<div class="form-group">
<label class="h2" for="where">Where Is Your Wedding?</label>
<p id="vendor-where-description">If left blank, vendors in all available locations will be shown.</p>
<div class="input-group-lg">
<input id="where" ng-model="formData.where" class="form-control" type="text" placeholder="Boston, MA" aria-describedby="vendor-where-description" />
</div>
</div>
<ul class="list-inline">
<li>
<a ui-sref="wizard.what#" class="btn btn-block btn-primary">
Next <span class="fa fa-arrow-right"></span>
</a>
</li>
</ul>
I created working plunker here
NOTE: You should read about state nesting and named views more. Because the current state and view definition is simply wrong.
Nested States & Nested Views
Multiple Named Views
Firstly, we should not use the ONE state definition with many views: {}. But we should split them into real states. Hierarchy will have three levels
The first level - super root state
.state( 'home', {
url: '/home',
views: {
"main": {
controller: 'HomeCtrl',
templateUrl: 'home/home.tpl.html'
},
}
})
The second level - wizzard, check that now we change the url. We will inherit its first part from our parent (home)
.state("wizard", {
parent: 'home',
//url: '/home/wizard',
url: '/wizard',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard.tpl.html'
})
The third level - all where, what, when now will also inherit url. They do not have to define parent, because it is part of their names
.state( "wizard.where", {
//url: '/home/wizard/where',
url: '/where',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard-where.tpl.html',
//parent: wizard
})
.state( "wizard.what", {
//url: '/home/wizard/what',
url: '/what',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard-what.tpl.html',
//parent: wizard
})
.state( "wizard.when", {
//url: '/home/wizard/when',
url: '/when',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard-when.tpl.html',
//parent: wizard
})
Parent wizzard must now contain unnamed view target ui-view=""
<div ui-view=""></div>
Current wizard.tpl.html contains this:
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view="wizard.where#"></div>
The sign # should be avoided, because it could be used for absulte view naming - BUT inside of the state defintion. So, what could work is ui-view="someName
<!-- our nested state views will be injected here -->
<div id="form-views" ui-view="someName"></div>
Now, these are (in example here) view content of the home.tpl
<div>
<h1>HOME</h1>
<div ui-view=""></div>
</div>
And wizzard.tpl
<div>
<h2>WIZZARD</h2>
<div ui-view=""></div>
</div>
So, we have unnamed view target inside of home and wizard states, That is very handy, because we can use the light state definition, without views : {} object. And that is always preferred in case we do not have multi-views.
That means, that this state definition will properly be injected into above template:
// no views - search in parent for a ui-view=""
...
.state( "wizard.when", {
url: '/when',
controller: 'VendorsCtrl',
templateUrl: 'vendors/wizard-when.tpl.html',
})
...
Check the doc:
View Names - Relative vs. Absolute Names
Behind the scenes, every view gets assigned an absolute name that follows a scheme of viewname#statename, where viewname is the name used in the view directive and state name is the state's absolute name, e.g. contact.item. You can also choose to write your view names in the absolute syntax.
For example, the previous example could also be written as:
.state('report',{
views: {
'filters#': { },
'tabledata#': { },
'graph#': { }
}
})
Notice that the view names are now specified as absolute names, as opposed to the relative name. It is targeting the 'filters', 'tabledata', and 'graph' views located in the root unnamed template. Since it's unnamed, there is nothing following the '#'. The root unnamed template is your index.html.
Calling the state from state
Whe we want in where state navigate to when, we can use directiv ui-sref, but it must contain state name, not view naming convention
// instead of this
<a ui-sref="wizard.what#"
we need this
<a ui-sref="wizard.what"
The reason, that in this three level hierarchy we do use only parent and child names (not grand parent 'home'), is hidden in state definition. Because we used this:
.state("wizard", {
parent: 'home',
Parent is just a parent, not part of the state name. Which is good in scenarios like this (we need the root/grand parent to establish some comon stuff, but it name is not needed for substates)
Check the doc:
ui-sref
A directive that binds a link (<a> tag) to a state. If the state has an associated URL, the directive will automatically generate & update the href attribute via the $state.href() method. Clicking the link will trigger a state transition with optional parameters.
...
You can specify options to pass to $state.go() using the ui-sref-opts attribute. Options are restricted to location, inherit, and reload.
ui-sref - string - 'stateName' can be any valid absolute or relative state
[S]tep one is embedded on the homepage rather than being a separate state
You should treat each ui-view as a state, but declare wizard.where as the default/index state.
Note that the tutorial uses $urlRouterProvider to make form/profile the default state.
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/form/profile');
In this manner, however, /form will end up as /form/profile.
You may, however, create an empty URL state with minor modification:
// route to show our basic form (/form)
.state('form', {
url: '/form',
templateUrl: 'form.html',
controller: 'formController',
abstract: true //<-- Declare parent as an abstract state.
})
// nested states
// each of these sections will have their own view
// url will be nested (/form)
.state('form.profile', {
url: '', //<-- Empty string for "profile" state to override the /form abstract state
templateUrl: 'form-profile.html'
})
// catch all route
// send users to the form page
$urlRouterProvider.otherwise('/form'); //<-- Default state is empty
#radim-köhler has also provided great insight into UI-Router and state definitions.
Though the states are changing successfully, I can't get the ui-view to update:
HTML
<a ui-sref="promo.foo">Foo state</a>
<a ui-sref="promo.bar">Bar state</a>
<div ui-view=""></div>
JavaScript
$urlRouterProvider.otherwise("/");
$stateProvider
.state('promo', {
url: "/",
abstract: true
})
.state('promo.foo', {
url: "promo/foo",
template: "'<h3>Foo</h3>'",
controller: function($scope) {
$scope.value = 'foo';
}
})
.state('promo.bar', {
url: "promo/bar",
template: "'<h3>Bar</h3>'",
controller: function($scope) {
$scope.value = 'bar';
}
})
Plnkr (with bootstrap styling)
I have also tried setting ui-view to equal specific states also; and to dynamically change its RHS from my $scope.
Since you're trying to create nested views, the home state should have a template with an ui-view-element in it.
Also you have both double and single quotes in the template
template: "'<h3>Foo</h3>'"
Is this Plunker what you had in mind?
Give your abstract state a template containing inside which your sub states will render ... eg http://scotch.io/tutorials/javascript/angular-routing-using-ui-router