Nested templates using ui-view - angularjs

I'm trying to display a nested template using ui-view.
AngularJS routing config
angular.module('myApp')
.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('home', {
url: '',
abstract: true
})
.state('home.default', {
parent: 'home',
url: '/home',
data: {
pageTitle: 'Homepage'
},
views: {
'content#': {
templateUrl: 'app/default/default.html',
controller: 'defaultController',
controllerAs: 'defaultController'
}
}
})
.state('default.subview', {
parent: 'default',
url: '/default/subview',
data: {
pageTitle: 'Homepage - subview'
},
views: {
'content#': {
templateUrl: 'app/subview/subview.html',
controller: 'subviewController',
controllerAs: 'subviewController'
}
}
})
;
}]);
Home: /#/home
<!-- this URI should be #/home -->
<h2>Homepage</h2>
<select>
<option>Subview</option>
</select>
<hr>
<!-- nested subview -->
<div ui-view=""></div>
Subview: /#/home/subview
<h2>Subview</h2>
So basically, I want the parent view (home) and subview's content to be included when I visit (/#/home/subview). However, only the subview content is being displayed.
Any tips on how to correctly utilize ui-view and nested subviews in AngularJS?

Your subview has to be a child of home and you set the subview with 'content#' to an defined ui-view wich replaces your view from home.
And I edited some copy paste issue since it looks like your home route was called default before
.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('home', {
url: '/home',
abstract: true,
template: '<ui-view/>'
})
.state('home.default', {
url: '/home/default',
data: {
pageTitle: 'Homepage'
},
views: {
'': {
templateUrl: 'home.html',
controller: 'defaultController',
controllerAs: 'defaultController'
}
}
})
.state('home.subview', {
parent: 'home',
url: '/subview',
data: {
pageTitle: 'Homepage - subview'
},
views: {
'': {
templateUrl: 'subview.html',
controller: 'subviewController',
controllerAs: 'subviewController'
}
}
});
}]);
Edit:
I created a Plunker with an working configuration, there was some more issues with that abstract home state (I never get it to work as expected) but if you click the links everything appears as expected.
Plunker

There really is no need for the views section if you have only one ui-view
angular.module('myApp')
.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('home', {
url: '',
abstract: true,
template: '<ui-view></ui-view>'
})
.state('home.default', {
// parent: 'home', // No need to set parent if you already prefixed state name
url: '', // The default subview of an abstract view should have '' for url
data: {
pageTitle: 'Homepage'
},
templateUrl: 'app/default/default.html',
controller: 'defaultController',
controllerAs: 'defaultController'
})
.state('home.default.subview', {
// parent: 'default', // No ned for parent
url: '/subview', // Only pu the part of the url here that is added to the parent'ls url
data: {
pageTitle: 'Homepage - subview'
},
templateUrl: 'app/subview/subview.html',
controller: 'subviewController',
controllerAs: 'subviewController'
})
;
}]);
In addition I've also changed the ui-sref in index.html
<a ui-sref="home.default.subview">Subview Route</a>
And the ui-view in home.html
<!-- nested subview -->
<ui-view></ui-view>
Check this plunker:
https://plnkr.co/edit/vEDYvXhp5mNjVT0yLRJN?p=preview

Related

Unable to navigate to child state in ui router

I have state as follow. I can navigate from app to app.child1. But I cannot navigate from app.child1 to app.child1.child2. There is no error in browser console. Do note that I am developing an ionic application but i dont think that this is ionic issue.
app.state('app', {
url: '/app',
abstract: true,
templateUrl: '',
controller: ''
})
.state('app.child1', {
url: '/app',
views: {
'menuContent': {
templateUrl: '',
controller: ''
}
}
})
.state('app.child1.child2', {
url: '/app/child1/child2',
views: {
'menuContent': {
templateUrl: '',
controller: ''
}
}
})
Navigation:
<button ui-sref="app.child1.child2">Change Page</button>
There is a working plunker
There are few issues with the state definition, which are covered in comments below:
.state('app', {
url: '/app',
abstract: true,
templateUrl: 'app.tpl.thml',
controller: 'appCtrl'
})
.state('app.child1', {
// child1 should have url part /child1
// while the /app is inherited from parent
//url: '/app',
url:'/child1',
views: {
'menuContent': {
templateUrl: 'child1.tpl.html',
controller: 'child1Ctrl'
}
}
})
.state('app.child1.child2', {
// all parts are inherited from parents
// so just /child2 is enough
//url: '/app/child1/child2',
url: '/child2',
views: {
// we target named view in a grand parent
// not in parent, we need to use the absolute name
// 'menuContent': {
'menuContent#app': {
templateUrl: 'child2.tpl.html',
controller: 'child2Ctrl'
}
}
})
These links will work now
<a href="#/app/child1">
<a href="#/app/child1/child2">
<a ui-sref="app.child1">
<a ui-sref="app.child1.child2">
Check it in action here
Your states don't have a template (templateUrl: ''). They should have a template with ui-view directive where it's child state can be injected.

How to display ui-router state on whole page?

I'm using ui-router for state handling. This works fine, but now I have to create page 404 and would like to display it on the whole page and not inside the page as other pages.
app.config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
function ($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.hashPrefix('!').html5Mode({
enabled: true,
requireBase: false
});
$stateProvider
.state('stateIndex', {
url: '/',
templateUrl: '/templates/list.html',
controller: 'dashListController'
})
.state('stateList', {
url: '/list',
templateUrl: '/templates/list.html',
controller: 'dashListController'
}).state('stateDashboard', {
url: '/dashboard/:id',
templateUrl: '/templates/dashboard.html',
controller: 'dashboardController'
})
.state('stateWidgetsList', {
url: '/widgetsList',
templateUrl: '/templates/widgetsList.html',
controller: 'widgetsListController'
})
.state('404', {
url: '/404',
templateUrl: '/templates/404.html'
});
}]);
and on my index.html I have
<div ui-view></div>
where I display all the pages, outside of this I have logo, menu, etc.. which I would like to hide while displaying 404 page.
How can I do it?
Personally I would redesign the index.html, and bring the outer template (logo, menu, etc), into it's own template and state. Then you can sit child states below it in the ui-router hierarchy. For example:
$stateProvider
.state('app', {
abstract: true,
url: '',
templateUrl: '/templates/appcontainer.html'
})
.state('app.stateIndex', {
url: '/',
templateUrl: '/templates/list.html',
controller: 'dashListController'
})
.state('404', {
url: '/404',
templateUrl: '/templates/404.html'
});
Then you just need to put your logos/menus, etc inside appcontainer.html, and then just have a single <div ui-view></div> inside your index.html. Also if you do it this way, don't forget to add the child ui-view inside appcontainer.html.
You can create a root parent state that will contain your layout stuff (logo, menu, etc) and have the 404 live outside of that.
routes
$stateProvider
.state('root', {
abstract: true, // makes this state not directly accessible
templateUrl: 'root.html'
})
.state('root.stateIndex', {
url: '/',
templateUrl: '/templates/list.html',
controller: 'dashListController'
})
// ...
.state('404', {
url: '/404',
templateUrl: '/templates/404.html'
});
root.html
<nav><!-- menu stuff --></nav>
<ui-view></ui-view>
<footer></footer>

Angular routing ui-view inside another ui-view

Conception overview:
We have two tabs on index html. There we routing those tabs like that:
<div ui-view></div>
On a second tab we have a selector, that switch tab's content in another ui-view like that:
<div ui-view="{{vm.currentView}}"></div>
where vm.currentView is a name of routing state ('book1' and etc.).
.state('tab2', {
abstract: true,
templateUrl: 'tab2.html',
controller: 'Tab2Controller',
controllerAs: 'vm'
})
.state('tab2.content', {
url: '/tab2',
views: {
'': {
templateUrl: 'tab2.html'
},
'book1#tab2': {
templateUrl: 'tab2-book1.html'
},
'book2#tab2': {
templateUrl: 'tab2-book2.html'
},
'book3#tab2': {
templateUrl: 'tab2-book3.html'
},
'book4#tab2': {
templateUrl: 'tab2-book4.html'
}
}
});
Everything is fine, except one thing: data content and name of a view is changing, but a template content isn't.
I resolved it by another way (based on exclude 'ui-view inside another ui-view' conception and separate views in states). But i still want to know: "How to do this with using 'ui-view inside ui-view' conception?"
Here's a Plunker Example
Its possible to make 'ui-view inside another ui-view'.
Lets say you have an index.html
<div ui-view="content"></div>
and state provider is like this :-
$stateProvider
.state('books', {
parent: 'pages',
url: '/books',
views: {
'content#': {
templateUrl: 'books.html',
controller: 'BooksController'
}
}
})
In books.html you have some links and another ui-view (nested ui-view). On click of links populate the nested ui-view.
books.html
<div>
<a ui-sref="book1"></a>
<a ui-sref="book2"></a>
<a ui-sref="book3"></a>
</div>
<!-- nested ui-view -->
<div ui-view="bookDetails"></div>
now state provider is :-
$stateProvider
.state('books', {
parent: 'pages',
url: '/books',
views: {
'content#': {
templateUrl: 'books.html',
controller: 'BooksController'
}
}
})
.state('book1', {
parent: 'books',
views: {
'bookDetails#books': {
templateUrl: 'book1.html',
controller: 'BookOneController'
}
}
})
.state('book2', {
parent: 'books',
views: {
'bookDetails#books': {
templateUrl: 'book2.html',
controller: 'BookTwoController'
}
}
})
.state('book3', {
parent: 'books',
views: {
'bookDetails#books': {
templateUrl: 'book3.html',
controller: 'BookThreeController'
}
}
})
bookDetails#books :- populate 'bookDetails' ui-view in 'books' state or we can say that find 'bookDetails' ui-view inside 'books' state and populate it with 'views' object.
As i explained earlier i just want to make 'ui-view inside another ui-view', but it seems impossible. I found two ways to resolve this "bug"(?).
First way: Exclude 'ui-view inside another ui-view' and use 'ng-include'
Simplest variant with minimal change of code. As you see here, i replaced
<div ui-view="{{vm.currentView}}"></div>
with
<ng-include src="vm.getTemplateUrl(vm.selectedBook.id)"/>
and add function to controller, thats switch templates:
function getTemplateUrl(id) {
switch (id) {
case 0:
return 'tab2-book1.html';
case 1:
return 'tab2-book2.html';
case 2:
return 'tab2-book3.html';
case 3:
return 'tab2-book4.html';
default:
return 'tab2-book4.html';
}
}
Second way: Formally save 'ui-view inside another ui-view' and separate views by states
And as you see here, formally i save 'ui-view inside ui-view', but in fact i just fully replace single ui-view by template from another single ui-view (can't set second ui-view by name).
$urlRouterProvider
.when('/tab2', '/tab2/book4');
$stateProvider
.state('tab2', {
url: '/tab2',
templateUrl: 'tab2.html'
})
.state('tab2.book1', {
url: '/book1',
params: {
id: 0
},
templateUrl: 'tab2-book1.html',
controller: 'Tab2Controller',
controllerAs: 'vm'
})
.state('tab2.book2', {
url: '/book2',
params: {
id: 1
},
templateUrl: 'tab2-book2.html',
controller: 'Tab2Controller',
controllerAs: 'vm'
})
.state('tab2.book3', {
url: '/book3',
params: {
id: 2
},
templateUrl: 'tab2-book3.html',
controller: 'Tab2Controller',
controllerAs: 'vm'
})
.state('tab2.book4', {
url: '/book4',
params: {
id: 3
},
templateUrl: 'tab2-book4.html',
controller: 'Tab2Controller',
controllerAs: 'vm'
});
Where content of tab2.html is:
<ui-view class="page-container"></ui-view>
When selector changed i call vm.changeHandbook(vm.selectedBook) to switch templates:
function changeHandbook(ref) {
$state.go(ref.value);
}
This is most weird and difficult way, but in the end we get more cleaner code.

Is there a better way to go about building layouts using ui.router?

I am currently using <div ng-include src="'js/app/partials/layout/header.html'"></div> just above my <div ui-view> in my index.blade.php file while using Angular with Laravel.
I have looked into parent state inheritance in ui.router but it seems to not work, and feels complicated / or perhaps an overkill for layouts. I just want to inject a header and a footer.
This is what I was doing earlier in my attempt to use ui.router states to create a layout injection system. As you can see below.
<div ui-view="header"></div>
<div ui-view></div>
.state('root', {
url: '/',
abstract: true,
views: {
'header': {
templateUrl: 'js/app/partials/header.html'
}
},
data: {
requireLogin: false
}
})
.state('root.login', {
url: '/login',
templateUrl: 'js/app/partials/login.html',
controller: 'LoginCtrl',
data: {
requireLogin: false
}
})
You need to change your structure of your html, by making named views & those will be specified with templateUrl & controller from views option of the state.
Basically inside your home.html you would have three named views such as header, content & footer, root state is setting header & footer templates with controlllers. Then your child state login will set the content view by using absolute state name using content#root in this #root because content named view has been loaded inside root state.
Markup
<div ui-view="header"></div>
<div ui-view="content"></div>
<div ui-view="footer"></div>
Code
myApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/login');
$stateProvider
.state('root', {
abstract: true,
url: '/',
//templateUrl: 'js/app/partials/home.html',//remove this
views: {
'': {
templateUrl: 'js/app/partials/home.html' //add it here
},
'header': {
templateUrl: 'js/app/partials/header.html'
},
'footer': {
templateUrl: 'js/app/partials/header.html'
}
},
data: {
requireLogin: false
}
})
.state('root.login', {
url: 'login',
views: {
'content#root': {
templateUrl: 'js/app/partials/login.html',
controller: 'LoginCtrl',
},
},
data: {
requireLogin: false
}
})
});
Working Plunkr
I Think you use this.
`.state('header', {
abstract : true,
templateUrl: 'js/app/partials/header.html'
})
.state('home', {
url: '/',
templateUrl: 'js/app/partials/home.html',
parent : 'header',
data: {
requireLogin: false
}
})
.state('login', {
url: '/login',
parent : 'header',
templateUrl: 'js/app/partials/login.html',
controller: 'LoginCtrl',
data: {
requireLogin: false
}
})`

Angular UI-Router multiple views

I am using angular UI-Router. I have the following in my route config
.config(function config($stateProvider) {
$stateProvider.state('newsFeedView', {
url: '/newsFeed',
controller: 'newsFeedController',
templateUrl: '../src/app/bulletinBoard/views/newsFeed.part.html',
data: {
pageTitle: 'News Feed'
}
})
.state('tradeFeedView', {
url: '/tradeFeed',
controller: 'tradeFeedController',
templateUrl: '../src/app/bulletinBoard/views/tradeFeed.part.html',
data: {
pageTitle: 'Trade Feed'
}
})
.state('bulletinBoard', {
url: '/bulletinBoard',
views: {
'tradeFeed': {
url: "",
controller: 'tradeFeedController',
templateUrl: '../src/app/bulletinBoard/views/tradeFeed.part.html'
},
'newsFeed': {
url: "",
controller: 'newsFeedController',
templateUrl: '../src/app/bulletinBoard/views/newsFeed.part.html'
}
},
templateUrl: '../src/app/bulletinBoard/views/bulletinBoard.part.html'
});
})
In my index page I just invoke the view using:
<div class="container" ui-view></div>
In My bulletinBoard.html i want to have a nested view:
<div ui-view="tradeFeed"></div>
<div ui-view="newsFeed"></div>
For the /newsFeed page and the /tradeFeed pages this works perfectly but for the bulletin board i can't see anything on the page. Where am i going wrong?
I find the example on the official GitHub wiki to be very unintuitive. Here is a better one:
https://scotch.io/tutorials/angular-routing-using-ui-router
For instance:
...
.state('bulletinBoard', {
url: '/bulletinBoard',
views: {
// the main template will be placed here (relatively named)
'': { templateUrl: '../src/app/bulletinBoard/views/bulletinBoard.part.html' },
// the child views will be defined here (absolutely named)
'tradeFeed#bulletinBoard': { template: ..... },
// another child view
'newsFeed#bulletinBoard': {
templateUrl: ......
}
}
});
The syntax of each view attribute being viewName#stateName.
The .state() method's templateUrl is ignored when using the views object. See the ui-router wiki for more info:
https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views#user-content-views-override-states-template-properties

Resources