I'm new to angular and I'm trying to understand nested views concept.
Based on the example provided in their documentation: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
//home.html
<body>
<div ui-view="header"></div>
<div ui-view="settings"></div>
<div ui-view="content"></div>
</body>
I have settings.html which has a check box. If it's checked it will load in the view(not named) the advanced settings template if not it will load the basic template
//settings.html
<input type="checkbox" ng-change="change()" ng-model="advancedSettings" />
<div ui-view></div>
so far I have defined something like this:
$stateProvider
.state('home', {
views: {
'header': {},
'settings': {
templateUrl: 'settings.html'
},
'content': {},
}
})
since I have 2 templates basicSettings.html and advancedSettings.html that I need to load in the view from settings.html based on that checkbox, I thought I have to declare something like this:
.state('settings#home.basic',(){
templateUrl: 'basicSettings.html'
});
but it's not working, instead I receive a lot of errors on console. How is the best way to implement this, without removing names from homepage views(header,settings,content), also how do I change the view based on the check box?
Thanks
There is a working plunker
Solution here could be with states defined like this:
$stateProvider
.state('home', {
abstract: true,
url: "/home",
views: {
'header': {
template: "This is HEADER"
},
'settings': {
templateUrl: 'settings.html',
controller: 'HomeCtrl',
},
'content': {
template: "This is CONTENT"
},
}
})
.state('home.basic', {
url: "/basic",
templateUrl: 'basicSettings.html'
})
.state('home.advanced', {
url: "/advanced",
templateUrl: 'advancedSettings.html'
})
we have parent state "home" and two children. These are triggered on change by 'HomeCtrl', e.g. like this:
.controller('HomeCtrl', ['$scope', '$state',
function($scope, $state) {
$scope.advancedSettings = false;
$scope.change = function(){
var childState = $scope.advancedSettings
? "home.advanced"
: "home.basic";
$state.go(childState);
}
}])
So, based on the setting, the view target "settings" and its ui-view="" (unnamed one) is filled with a child state - basic or advanced
Check it here
Related
I would like to have a common state with all the common views like the header and the sidebar, and a template where I would like to load different views that can change when the state is changing.
I have an index HTML file like this:
...
<div ui-view="header"></div>
<div ui-view="sidebar"></div>
<div class="container">
<div class="wrapper">
<div ui-view="content"></div>
</div>
</div>
...
While the AngularJS config is something like this:
$stateProvider
.state('mainCommonState', {
url: '',
abstract: true,
views: {
header: {
templateUrl: 'app/common/header.html',
controller: 'headerCtrl',
controllerAs: 'vm'
},
sidebar: {
templateUrl: 'app/common/sidebar.html',
controller: 'sidebarCtrl',
controllerAs: 'vm'
},
content: {}
},
resolve: {
apiEnvironment: function ($q, environmentApiService) {
var deferred = $q.defer();
deferred.resolve(environmentApiService.getApiEnvironment());
return deferred.promise;
}
}
})
.state('first-page-content', {
url: '/first-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/first-page-content.html'
controller: 'firstPageCtrl',
controllerAs: 'vm'
}
}
})
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
.state('third-page-content', {
url: '/third-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/third-page-content.html'
controller: 'thirdPageCtrl',
controllerAs: 'vm'
}
}
})
For some reason this is not working: I have an empty view instead of the 3 templates that I would like to show in the content ui-view.
If I define a template (even a blank template) inside the the abstract state, the view that is always showing is the one inside the abstract state mainCommonState.
Where am I wrong?
1st Edit: UPDATE Following the first answer
Following the suggestion from Chris T, I have updated my code, but there still something missing.
I have created a Plunker so you can help me fixing the issues.
2nd Edit
Following the suggestions from Chris T, I have updated the code using the absolute path for the content view and now the contents are switching correctly.
I have updated the Plunker accordingly to that and introduced a new level of nesting view (tabs in the first page), and I would like to have the first tab active when the first page content is loaded.
If I follow these solutions and set empty the url of the first page and set it to the first tab instead, this is not working.
Any suggestions?
Your views are targeting the wrong named ui-view.
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
content: {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
In this snippet, it targets the ui-view named content from the parent state which is mainCommonState. However, the content ui-view was not created in the mainCommonState. It was created in the root template.
Change your view declarations to target the view at the correct state, for example this targets the content view at the root state (which is named empty string):
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
'content#': {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
In ui-router 1.0 and higher you can also use absolute ui-view names by prefixing with an exclamation
.state('second-page-content', {
url: '/second-page-content',
parent: 'mainCommonState',
views: {
'!content': {
templateUrl: 'app/components/second-page-content.html'
controller: 'secondPageCtrl',
controllerAs: 'vm'
}
}
})
Read more about view targeting in the UI-Router views guide:
https://ui-router.github.io/guide/views#view-name-only
I am trying to make a nested views, here is the plunker https://embed.plnkr.co/oRMnMW4QoWwhSkm9maHf/. The state changes but the template not changes.
Can anyone correct me what have I done wrong
Goto Link > Second Nested .
On Clicking the button , state changes successfully but the content is not injected. I want the link page content to be replaced by the second-nested content
Try to put abstract:true on the 'father' root like:
var routerApp = angular.module('routerApp', ['ui.router','ncy-angular-breadcrumb']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home/list');
$stateProvider
// HOME STATES AND NESTED VIEWS ========================================
.state('home', {
url: '/home',
abstract: true,
templateUrl: './partial-home.html'
})
// nested list with custom controller
.state('home.list', {
url: '/list',
templateUrl: './partial-home-list.html',
controller: function($scope) {
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
}
})
.state('home.second', {
url: '/second',
templateUrl: './second.html',
});
});
routerApp.run(['$rootScope', '$state', function ($rootScope, $state) {
$rootScope.$on('$stateChangeStart', function (event, toState) {
console.log("state Change")
});
}]);
but Remember .. if you put abstract: true .. the url is not a real one .. it's a prefix .. or as i call it .. a father of other routing ..so you can not call it in the .otherwise()
And in your link (and routing) of the second view .. just remove the .list ... so like this:
.state('home.second', { //<-- HERE .. REMOVE THE .list
url: '/second',
templateUrl: './second.html',
});
and in the link:
// AND HERE ..
<a ui-sref="home.second" class="btn btn-danger">Second Nested</a>
Answer is pretty simple - you are initializing 3rd level of nesting states but in ./partial-home-list.html you've didn't add ui-view directive.
Add <ui-view></ui-view> in ./partial-home-list.html and you will see that it works as you defined.
If you want to display home.list.second as separate page then define second state like this
.state('home.second', {
url: '/home/list/second',
templateUrl: 'second.html',
});
Remember to update ui-sref to home.second on button
--
Just to get nested breadcrumb I have not "nice" solution but will work.
-- Partial home list html
<div ng-if="state.current.name != 'home.list.second'">
<ul>
<li ng-repeat="dog in dogs">{{ dog }}</li>
</ul>
<div ncy-breadcrumb></div>
<a ui-sref="home.list.second" class="btn btn-danger">Second Nested</a>
</div>
<ui-view></ui-view>
App js
// nested list with custom controller
.state('home.list', {
url: '/list',
templateUrl: 'partial-home-list.html',
controller: function($scope, $state) {
$scope.state = $state;
$scope.dogs = ['Bernese', 'Husky', 'Goldendoodle'];
}
})
.state('home.list.second', {
url: '/second',
templateUrl: 'second.html',
});
Alright so I am having an issue with my named views loading content into my state.
Its mostly problematic because I really do not know why it isnt working; with angular that usually means a typo but I have recreated my problem in a plunker and am getting the same results so maybe I am missing something.
I know this question has been asked before:: however in all the results I saw on here people were putting content into an abstract state's children. what I want to do is have a state; populate it with named views as well as other content relevant to that state; then have that states children load content into the main states named views. should be simple enough and rather straight forward but alas mine will not work for me.
Here is the link to the plunker made:: http://plnkr.co/edit/TWCQuoIyJRvTb42Z7xxe?p=preview
as you will see the main 'papers' state is loading. however none of the content from the named views is being loaded into the 'papers' state from its child state 'papers.views'.
Code for reference:
Module declaration and state config(app.js)
var app = angular.module( 'app', [ 'ui.router' ] );
app.config(['$stateProvider', '$urlRouterProvider',
function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/papers');
// States
$stateProvider
.state( 'papers', {
url: "/papers",
templateUrl: 'papers.html'
}) // nested paper state + views
.state( 'papers.views', {
views: {
'#papers': {
templateUrl: 'papers.home.html'
},
'paper1#papers': {
templateUrl: 'papers.paper1.html'
},
'paper2#papers': {
templateUrl: 'papers.paper2.html'
}
}
})
}
])
.run(['$rootScope', '$state', '$stateParams',
function ($rootScope, $state, $stateParams) {
$rootScope.$state = $state;
$rootScope.$stateParams = $stateParams;
}])
Index page (papers.html loading here):
<body>
<div ui-view></div>
</body>
papers page ( where nested views are supposed to be loading )
<h1>This is the papers page. other views should load in here</h1>
<div ui-view ></div>
<div ui-view="paper1" ></div>
<div ui-view="paper2" ></div>
One way, how to fix this is to add two lines:
change your parent to be abstract : true and
force child to define url : ''
There is an upated and working plunker, this is the updated state def:
$urlRouterProvider.otherwise('/papers');
// States
$stateProvider
.state( 'papers', {
// NEW LINE
abstract: true,
url: "/papers",
templateUrl: 'papers.html'
}) // nested paper state + views
.state( 'papers.views', {
// NEW LINE - because parent is abstract, same url here - this will be loaded
url: '',
views: {
'#papers': {
templateUrl: 'papers.home.html'
},
'paper1#papers': {
templateUrl: 'papers.paper1.html'
},
'paper2#papers': {
templateUrl: 'papers.paper2.html'
}
}
})
More details about this in documentation:
How to: Set up a default/index child state
Check it here
Another way could be to change the default:
$urlRouterProvider.otherwise('/papers/view');
And add url: '/view' to child state:
...
.state( 'papers.views', {
url: '/view',
views: {
...
Check this version here
I just started migrating to AngularJS and I'm already having problems with the $stateProvider from the angular-ui/ui-router framework. This is what I have so far:
angular
.module('testApp', ['ui.router'])
.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/index/home");
$stateProvider
.state('index', {
abstract: true,
url: "/index",
template: "parent"
})
.state('index.home', {
url: "/home",
template: "child"
})
})
.controller("MainController", function() {
})
Now, running this script redirects me to http://example.com/#/index/home but it only displays the parent string on the page. The child string is not being shown. From my understanding this should load the first template because we are on the /#/index domain part and then the second because we are on a nested page /#/index/home.
Can someone help me and explain why this is not working as intended?
In your template for the parent you need another <div ui-view></div> in order to render child states.
If you want multiple nested views, you can have multiple ui-view in your parent template. You just have to name them. For example,
parent template:
<h1>parent</h1>
<div ui-view="child1"></div>
<div ui-view="child2"></div>
And you define the child states as follows:
$stateProvider
.state('index', {
abstract: true,
url: "/index",
template: "parent"
})
.state('index.home', {
url: "/home",
views: {
'child1': {
templateURL: "child1.html"
}
}
})
.state('index.home2', {
url: '/home2',
views: {
'child2': {
templateURL: 'child2.html'
}
}
})
*note I used templateURL instead of template. Assuming your app has modular file structure.
I have a problem with ui-router for angular.js.
I am currently working on a project with angular.js and using ui-router as router.
The problem now is, that I want to have a nested view as like this:
views/settings/index.html (also an previously created template in ui-router)
<div class="settings">
[...]
<div class="settings-content" ui-view="content"></div>
</div>
app.js
$stateProvider.state('settings', {
url: '/settings',
views: {
main: {
templateUrl: 'views/settings/index.html',
controller: 'SettingsController'
},
"content": {
templateUrl: 'views/settings/privacy.html',
controller: 'SettingsController'
}
},
ncyBreadcrumb: { label: 'Settings' }
})
Now the problem I have is, that the defined content template is not injected into the ui-view="content" div. The main content (views/settings/index.html) is loading properly. And in nested states it's also possible to add a template into the ui-view="content" with the same "string": Object.
How does this come?
Thanks in advance!
You must use the viewName#stateName syntax as stated here.
Try this
$stateProvider.state('settings', {
url: '/settings',
views: {
main: {
templateUrl: 'views/settings/index.html',
controller: 'SettingsController'
},
"content#settings": {
templateUrl: 'views/settings/privacy.html',
controller: 'SettingsController'
}
},
ncyBreadcrumb: { label: 'Settings' }
})
I have used the ui-router module in my demo application.
You need to specify the hierarchy in your view.
The parent view is not specified to child view so you must use ViewName#parentState
https://github.com/singhmohancs/angularDemo/tree/master/app/modules