UI-Router is not compiling deep levels of templates - angularjs

I'm having problem building a nested template using UI-Router. It seems it is unable to load or compile templates in the third level (or deeper).
I've created a plunker with a simplified example, this is the code:
angular.module('plunker', ["ui.router"])
.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
$urlRouterProvider.otherwise("/");
$locationProvider.html5Mode(false).hashPrefix('!');
$stateProvider
.state('home', {
url: "/",
controller: 'MainCtrl',
views: {
'main': {
template: 'Main content'
},
'secondary': {
templateUrl: "view2.html",
views: {
'hot': {
template: 'hot content'
},
'cold': {
template: 'cold content'
}
}
}
}
})
})
.controller('MainCtrl', function($scope) {
$scope.sayHello = 'Hello';
})
where the html is
<body class='container' ng-controller='MainCtrl'>
<h2>Nested template proof</h2>
<div ui-view>
<p>{{sayHello}}</p>
<div ui-view="main"></div>
<div ui-view="secondary"></div>
</div>
</body>
and view2.html is
<div>
<p>view2 loaded</p>
<div ui-view="hot"></div>
<div ui-view="cold"></div>
</div>
can any one tell me what I'm doing wrong? the 'hot' and 'cold' sections are not being compiled.

There is an updated and working example
The nesting of views inside one state must be done differently. We would need absolute naming - so this would be the syntax:
.state('home', {
url: "/",
controller: 'MainCtrl',
views: {
'main': {
template: 'Main content'
},
'secondary': {
templateUrl: "view2.html",
//views: {}
},
'hot#home': {
template: 'hot content'
},
'cold#home': {
template: 'cold content'
}
}
})
This is the absolute view naming, discussed e.g. here:
Angular-UI Router: Nested Views Not Working
Angular UI Router - Nested States with multiple layouts
Angularjs ui-router not reaching child controller

Related

How can I make an angular-ui-router template accept two views?

I am trying to understand how to use ui-router views. Here's what I have so far:
var rootSubjectsSubjectAdminWordsWord = {
name: 'r.s.s.a.w.w',
template: '<div ui-view ></div>',
url: '/:wordId',
};
var rootSubjectsSubjectAdminWordsWordDelete = {
name: 'r.s.s.a.w.w.delete',
templateProvider: ['$templateCache', ($templateCache) => {
return $templateCache.get('/app/admin/word/word.html');
}],
url: '/delete',
};
/app/admin/word/word.html
<div>
<ng-include src="'/app/admin/word/wordData.html'"></ng-include>
<ng-include src="'/app/admin/word/wordForms.html'"></ng-include>
</div>
What I would like to do is to remove the need for having word.html.
Is there a way that could make the template '<div ui-view ></div>' accept the two HTML
files by somehow naming the views?
Here is per UI Router Docs. Basically inside where the views object is defined is where you'll name the view and point it to that view(see #1). After that router will take care of the rest.
$stateProvider
.state('report', {
views: {
//(#1)--here is the named router matching in the html.
'filters': {
templateUrl: 'report-filters.html',
controller: function($scope) {...controller stuff just
for filters view...
}
},
'tabledata': {
templateUrl: 'report-table.html',
controller: function($scope) {...controller stuff just
for tabledata view...
}
},
'graph': {
templateUrl: 'report-graph.html',
controller: function($scope) {...controller stuff just
for graph view...
}
}
}
});
<body>
<div ui-view="filters"></div>
<div ui-view="tabledata"></div>
<div ui-view="graph"></div>
</body>

my first ui-router app - why nested views not loading?

my app is here
http://ui-router2-dwilbank68.c9.io/
I know ui-router is loading correctly because the NAMED views are functioning correctly (click About). The nested views list and paragraph, however, are not working, and I've looked over the code a dozen times for discrepancies. No errors in the Chrome console either.
here is the view I am trying to do the nesting in...
<div class="jumbotron text-center">
<h1>The Homey Page</h1>
<p>This page demonstrates nested views.</p>
<a ui-sref='.list' class='btn btn-primary'>List</a>
<a ui-sref='.paragraph' class='btn btn-primary'>Paragraph</a>
</div>
and here is my entire javascript content
var routerApp = angular
.module('routerApp', ['ui.router']);
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
$stateProvider
// HOME STATES AND NESTED VIEWS ========================================
.state('home', {
url: '/home',
templateUrl: 'partial-home.html'
})
.state('home.list', {
url: '/list',
templateUrl: 'partial-home-list.html',
controller: function($scope){
$scope.dogs = ['Bernese','Husky','Goldendoodle'];
}
})
.state('home.paragraph', {
url: '/paragraph',
template: 'I could use a taco right now'
})
// ABOUT PAGE AND MULTIPLE NAMED VIEWS =================================
.state('about', {
url: '/about',
views: {
'': {templateUrl: 'partial-about.html'},
'columnOne#about': { template: 'Look I am a column' },
'columnTwo#about': {
templateUrl: 'table-data.html',
controller: 'scotchController'
}
}
});
});
routerApp
.controller('scotchController', function($scope){
$scope.message = 'test';
$scope.scotches = [
{ name: 'Macallan 12', price: 50 },
{ name: 'Chivas Regal Royal Salute', price: 10000 },
{ name: 'Glenfiddich 1937', price: 20000 } ];
});
The url DOES change... but why do the templates not load??
You probably missed <div ui-view></div> in your home view (partial-home.html)

How to hide content in the parent view when the user goes to a nested state and show it again when the user backs to the parent one?

I use ui-router and have two nested views.
I’d like to hide some html-content in the parent view when the user goes to child state and show it again when the user backs to parent one.
Could anybody give an advice how to achieve that?
It’s easy to do that using root scope and state change events but it looks like a dirty way, doesn’t it?
app.js
'use strict';
var myApp = angular.module('myApp', ['ui.router']);
myApp.controller('ParentCtrl', function ($scope) {
$scope.hideIt = false;
});
myApp.controller('ChildCtrl', function ( $scope) {
$scope.$parent.hideIt = true;
});
myApp.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider
.state('parent', {
url: '/parent',
templateUrl: 'parent.html',
controller: 'ParentCtrl'
})
.state('parent.child', {
url: '/child',
template: '<h2>Child view</h2>',
controller: 'ChildCtrl'
});
});
parent.html
<h2>Parent view</h2>
<div ng-hide="hideIt">
<p>This text should be hidden when the user goes to the nested state.</p>
</div>
<a ui-sref="parent.child">Go to the nested view</a>
<div ui-view></div>
One simple solution is to fill ui-view tag in the parent template with the content that you want gone when child state is loaded.
<ui-view>
<h2>Parent view</h2>
<div ng-hide="hideIt">
<p>This text should be hidden when the user goes to the nested state.</p>
<a ui-sref="parent.child">Go to the nested view</a>
</ui-view>
You should check out named views for this. That would probably be the best way to go. https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
Also, there's another thread that answered this question over here: https://stackoverflow.com/a/19050828/1078450
Here's the working code for nested named views, taken from that thread:
angular.module('myApp', ['ui.state'])
.config(['$stateProvider', '$routeProvider',
function($stateProvider, $routeProvider) {
$stateProvider
.state('test', {
abstract: true,
url: '/test',
views: {
'main': {
template: '<h1>Hello!!!</h1>' +
'<div ui-view="view1"></div>' +
'<div ui-view="view2"></div>'
}
}
})
.state('test.subs', {
url: '',
views: {
'view1#test': {
template: "Im View1"
},
'view2#test': {
template: "Im View2"
}
}
});
}
])
.run(['$state', function($state) {
$state.transitionTo('test.subs');
}]);
http://jsfiddle.net/thardy/eD3MU/
Edit:
Adding some thoughts re the angular-breadcrumbs comment. I haven't used it myself, but at first glance it seems like subroutes shouldn't break the breadcrumbs. I'm just looking at the source code of their demo, around line 62. I'd have to spin it all up to really go about testing it, but it looks like they're doing almost the same thing with specifying views, and it works there: https://github.com/ncuillery/angular-breadcrumb/blob/master/sample/app.js#L62
.state('room', {
url: '/room',
templateUrl: 'views/room_list.html',
controller: 'RoomListCtrl',
ncyBreadcrumb: {
label: 'Rooms',
parent: 'sample'
}
})
.state('room.new', {
url: '/new',
views: {
"#" : {
templateUrl: 'views/room_form.html',
controller: 'RoomDetailCtrl'
}
},
ncyBreadcrumb: {
label: 'New room'
}
})
.state('room.detail', {
url: '/{roomId}?from',
views: {
"#" : {
templateUrl: 'views/room_detail.html',
controller: 'RoomDetailCtrl'
}
},
ncyBreadcrumb: {
label: 'Room {{room.roomNumber}}',
parent: function ($scope) {
return $scope.from || 'room';
}
}
})
Edit2:
This solution will not combine routes into one crumb
See the official demo
re: But I use angular-breadcrumb and in your solution they will be combined into one crum.

Angular UI router - not calling the views

I'm trying out Angular UI router for the first time. I'm having issues where the views are not being called accordingly. Check the plunker: http://plnkr.co/edit/cBfR6u2BPJvKN16vi6hG?p=preview
Does anything stand out on the router?
deviceApp.config(function ($stateProvider) {
$stateProvider
.state('devices', {
views: {
'environment': {
template: 'Look I am a view!',
controller: 'DataCtrl'
},
'devicedetail': {
templateUrl: 'index.html',
controller: 'DeviceCtrl'
}
}
}
)}
);
perhaps the absence of a "url" definition in your view object?
'environment': {
url: '/environment',
template: 'Look I am a view!',
controller: 'DataCtrl'
},
You can do something like that :
config.js
.config(function ($urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$urlRouterProvider.when('', '/');
})
app.js
state('main', {
url: '/',
views: {
'': { templateUrl: 'app/main/main.html', controller: 'MainCtrl'},
'navbar': { templateUrl: 'components/navigation/navbar/navbar.html'},
}
index.html
<body>
<header ui-view="navbar" class="header"></header>
<section class="content">
<div ui-view></div>
</section>
</body>

Routing in angularjs for multiple controllers?

I'm trying to build a view - I've set up two controllers to practice, one's HeaderCtrl, with some data in it (site title, header background, etc), the other should have the main content of the page - MainCtrl.
When defining the route, I'm doing like so:
mainApp.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'MainCtrl',
templateUrl: 'modules/dashboard.html'
})
})
This works perfectly fine, but what I would want is to specify multiple parameters to this, something like this:
mainApp.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'HeaderCtrl',
templateUrl: 'modules/header.html'
},
{
controller: 'MainCtrl',
templateUrl: 'modules/dashboard.html'
})
})
This doesn't work so I'm guessing it's not the way to do it. What I'm actually asking - can you specify multiple controllers in $routeProvider ? Or what would be the correct way to build this view ?
My approach to this problem would be to have two directives - header and main which reference the corresponding templates.
For Example:
app.directive('header', function () {
return {
restrict: 'A',
replace: true,
templateUrl:'templates/header.html'
}
})
Then you can have a page that contains the directives - index.html.
<div header></div>
<div main></div>
Then have one controller that routes to your index.html
You can also encapsulate the header and main in separate header and main controllers if you want to deal with them separately.
e.g.
<div ng-controller="HeaderCtrl">
<div header></div>
</div>
<div ng-controller="MainCtrl">
<div main></div>
</div>
or with the templates themselves
What you might want to do is place your HeaderCtrl outside of ng-view and then have MainCtrl mapped to your index route (ie. '/'). If you needed to have multple controllers mapped to your index route, you can assign them within the template that you have mapped to that route. Here is what that would look like:
index.html
<html>
<body ng-app='yourApp'>
<div id="header" ng-controller="HeaderCtrl"></div>
<div ng-view></div>
</body>
</html>
app.js
angular.module('mainApp', []).
config(function ($routeProvider) {
$routeProvider.when('/', {
controller: 'MainCtrl',
templateUrl: 'modules/dashboard.html'
})
});
dashboard.html
<div ng-controller="SomeCtrl"></div>
<div ng-controller="SomeOtherCtrl"></div>
...or, if you really wanted to get creative, you could include ui-router from the AngularUI folks https://github.com/angular-ui/ui-router This would allow you to have multiple "views" and map them to your routes.
You should use angular's ui-router : https://github.com/angular-ui/ui-router, you can specify a controller and template for each "element" of your page :
myApp.config(function($stateProvider) {
$stateProvider
.state('index', {
url: "",
views: {
"viewA": { template: "index.viewA" },
"viewB": { template: "index.viewB" }
}
})
.state('route1', {
url: "/route1",
views: {
"viewA": { template: "route1.viewA" },
"viewB": { template: "route1.viewB" }
}
})
.state('route2', {
url: "/route2",
views: {
"viewA": { template: "route2.viewA" },
"viewB": { template: "route2.viewB" }
}
})
});
I don't think you can specify multiple controllers. I think what you're looking for is something like an 'index.html' template that refers to your header and dashboard:
<div id='header' ng:controller='HeaderCtrl' />
<div id='main' ng:controller='MainCtrl' />
To actually fill in with the right templates, I would use a directive. I think this is one of the most powerful parts of angular. You can create a header directive that you can reuse on all your pages.
Try this it should work
mainApp.config(function ($routeProvider) {
$routeProvider
.when('/',
{
controller: 'HeaderCtrl',
templateUrl: 'modules/header.html'
}).when('', //route here in the empty quotes
{
controller: 'MainCtrl',
templateUrl: 'modules/dashboard.html'
});
});

Resources