Angular ui-router nested view routers not work - angularjs

Why this routes not working? How to force this code work? How to implement layouts work in angular-ui-router? Please help resolve this problem.
$stateProvider
.state( 'layout', {
abstract: true,
url: '',
views: {
'layout': { templateUrl: 'template/layout.html' },
'header': {
templateUrl: 'template/header.html',
controller: 'HeaderController'
},
'sidebar': { templateUrl: 'template/sidebar.template.html' }
}
} )
.state( 'layout.home', {
url: '/',
views: {
'main#layout.home': { templateUrl: 'template/main.html' }
}
}
);
layout.html
<main class="layout">
<div ui-view="header"></div>
<div class="main">
<div class="container">
<div class="row">
<div class="col-md-9">
<div class="content">
<div ui-view="main"></div>
</div>
</div>
<div class="col-md-3">
<div ui-view="sidebar"></div>
</div>
</div>
</div>
</div>
</main>
index.html
<div ui-view="layout"></div>

This should work
.state( 'layout', {
abstract: true,
url: '',
views: {
'layout': { templateUrl: 'template/layout.html' },
'header': {
templateUrl: 'template/header.html',
controller: 'HeaderController'
},
//'sidebar': { templateUrl: 'template/sidebar.template.html' }
'sidebar#layout': { templateUrl: 'template/sidebar.template.html' }
}
} )
.state( 'layout.home', {
url: '/',
views: {
//'main#layout.home': { templateUrl: 'template/main.html' }
'main': { templateUrl: 'template/main.html' }
}
}
The parent state 'layout' - is now targeting 'sidebar#layout' - using absolute naming, to find a template inside of the 'template/layout.html'
The child view is simply using parents target 'main' so we do not need absolute naming. And if we want, it would be just
'main#layout': { templateUrl: 'template/main.html' }
because we target parent's 'layout' target ui-view="main"
Working example could be found here (with some more details)
Nested states or views for layout with leftbar in ui-router?

Try with this:
$stateProvider
.state( 'layout', {
abstract: true,
url: '',
views: {
'layout': {
templateUrl: 'template/layout.html'
}
}
} )
.state( 'layout.home', {
url: '/',
views: {
'main': {
templateUrl: 'template/main.html'
},
'header': {
templateUrl: 'template/header.html',
controller: 'HeaderController'
},
'sidebar': {
templateUrl: 'template/sidebar.template.html'
}
}
}
);

Related

AngularJS UI Router: redirect to other state isn't working (net::ERR_FAILED)

I am building an application using AngularJS and UI-Router for routing. Basically, users need to log in and based on the logic, the site will redirect users to different views.
My situation:
After username and password are provided, I am trying to redirect to another state in my login controller. However, I always receive an net::ERR_FAILED message.
I have tried $state.go() and $location.path(), but neither is working.
My question:
How should I set up the configuration to make it work?
Code in App.js
'use strict';
/**
* #ngdoc overview
* #name clientApp
* #description
* # clientApp
*
* Main module of the application.
*/
angular
.module('clientApp', [
'ngAnimate',
'ngCookies',
'ngResource',
'ngRoute',
'ngSanitize',
'ngTouch',
'toaster',
'ngFileUpload',
'ui.router',
'ui.bootstrap'
])
.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/');
$stateProvider.state('site', {
'abstract': true,
views: {
'navi#': {
templateUrl: "views/navigation.html",
controller: 'NavigationCtrl',
controllerAs: 'navigation'
}
},
resolve: {
authorize: ['authorization',
function(authorization) {
return authorization.authorize();
}
]
}
}).state('home', {
parent: 'site',
url: '/',
data: {
roles: []
},
views: {
'content#': {
templateUrl: "views/main.html",
controller: 'MainCtrl',
controllerAs: 'main'
}
}
}).state('register', {
parent: 'site',
url: '/register',
data: {
roles: []
},
views: {
'content#': {
templateUrl: 'views/register.html',
controller: 'RegisterCtrl',
controllerAs: 'register'
}
}
}).state('login', {
parent: 'site',
url: '/login',
data: {
roles: []
},
views: {
'content#': {
templateUrl: 'views/login.html',
controller: 'LoginCtrl',
controllerAs: 'vm'
}
}
}).state('about', {
parent: 'site',
url: '/about',
data: {
roles: []
},
views: {
'content#': {
templateUrl: 'views/about.html',
controller: 'AboutCtrl',
controllerAs: 'about'
}
}
}).state('products', {
parent: 'site',
url: '/products',
data: {
roles: []
},
views: {
'content#': {
templateUrl: 'views/products.html',
controller: 'ProductsCtrl',
controllerAs: 'products'
}
}
}).
state('product', {
parent: 'site',
url: '/product/:productCode',
data: {
roles: []
},
views: {
'content#': {
templateUrl: 'views/product.html',
controller: 'ProductCtrl',
controllerAs: 'product'
}
}
}).state('cart', {
parent: 'site',
url: '/cart',
data: {
roles: []
},
views: {
'content#': {
templateUrl: 'views/cart.html',
controller: 'CartCtrl',
controllerAs: 'cart'
}
}
}).state('productCockpit', {
parent: 'site',
url: '/productCockpit',
data: {
roles: ['Admin']
},
views: {
'content#': {
templateUrl: 'views/productcockpit.html',
controller: 'ProductcockpitCtrl',
controllerAs: 'productCockpit'
}
}
}).state('manageProduct', {
parent: 'site',
url: '/productCockpit/:productCode',
data: {
roles: ['Admin']
},
views: {
'content#': {
templateUrl: 'views/manageproduct.html',
controller: 'ManageproductCtrl',
controllerAs: 'manageProduct'
}
}
}).state('userCockpit', {
parent: 'site',
url: '/userCockpit',
data: {
roles: ['Admin']
},
views: {
'content#': {
templateUrl: 'views/usercockpit.html',
controller: 'UsercockpitCtrl',
controllerAs: 'userCockpit'
}
}
}).state('manageUser', {
parent: 'site',
url: '/userCockpit/:username',
data: {
roles: ['Admin']
},
views: {
'content#': {
templateUrl: 'views/manageuser.html',
controller: 'ManageuserCtrl',
controllerAs: 'manageUser'
}
}
}).state('accessdenied', {
parent: 'site',
url: '/denied',
data: {
roles: []
},
views: {
'content#': {
templateUrl: 'views/denied.html'
}
}
});
}
]);
angular.module("clientApp")
.run(['$rootScope', '$state', '$stateParams', 'authorization', 'principal',
function($rootScope, $state, $stateParams, authorization, principal) {
$rootScope.$on('$stateChangeStart', function(event, toState, toStateParams) {
$rootScope.toState = toState;
$rootScope.toStateParams = toStateParams;
if (principal.isIdentityResolved()){
authorization.authorize();
}
});
}
]);
Code in Login Controller:
'use strict';
/**
* #ngdoc function
* #name clientApp.controller:LoginCtrl
* #description
* # LoginCtrl
* Controller of the clientApp
*/
angular.module('clientApp')
.controller('LoginCtrl', function ($state, AuthenticationService) {
var vm = this;
function login() {
vm.dataLoading = true;
AuthenticationService.Login(vm.username, vm.password, function (response) {
if (response.success) {
AuthenticationService.SetCredentials(response.token);
$state.go('products');
} else {
AuthenticationService.ClearCredentials();
///FlashService.Error(response.message);
vm.dataLoading = false;
}
});
}
vm.login = login;
});
Html View:
<div class="col-md-6 col-md-offset-3">
<h2>Login</h2>
<div ng-show="vm.error" class="alert alert-danger">{{vm.error}}</div>
<form name="form" ng-submit="vm.login()" role="form">
<div class="form-group" ng-class="{ 'has-error': form.username.$dirty && form.username.$error.required }">
<label for="username">Username</label>
<input type="text" name="username" id="username" class="form-control" ng-model="vm.username" required />
<span ng-show="form.username.$dirty && form.username.$error.required" class="help-block">Username is required</span>
</div>
<div class="form-group" ng-class="{ 'has-error': form.password.$dirty && form.password.$error.required }">
<label for="password">Password</label>
<input type="password" name="password" id="password" class="form-control" ng-model="vm.password" required />
<span ng-show="form.password.$dirty && form.password.$error.required" class="help-block">Password is required</span>
</div>
<div class="form-actions">
<button type="submit" ng-disabled="form.$invalid || vm.dataLoading" class="btn btn-primary">Login</button>
<img ng-if="vm.dataLoading" src="data:image/gif;base64,R0lGODlhEAAQAPIAAP///wAAAMLCwkJCQgAAAGJiYoKCgpKSkiH/C05FVFNDQVBFMi4wAwEAAAAh/hpDcmVhdGVkIHdpdGggYWpheGxvYWQuaW5mbwAh+QQJCgAAACwAAAAAEAAQAAADMwi63P4wyklrE2MIOggZnAdOmGYJRbExwroUmcG2LmDEwnHQLVsYOd2mBzkYDAdKa+dIAAAh+QQJCgAAACwAAAAAEAAQAAADNAi63P5OjCEgG4QMu7DmikRxQlFUYDEZIGBMRVsaqHwctXXf7WEYB4Ag1xjihkMZsiUkKhIAIfkECQoAAAAsAAAAABAAEAAAAzYIujIjK8pByJDMlFYvBoVjHA70GU7xSUJhmKtwHPAKzLO9HMaoKwJZ7Rf8AYPDDzKpZBqfvwQAIfkECQoAAAAsAAAAABAAEAAAAzMIumIlK8oyhpHsnFZfhYumCYUhDAQxRIdhHBGqRoKw0R8DYlJd8z0fMDgsGo/IpHI5TAAAIfkECQoAAAAsAAAAABAAEAAAAzIIunInK0rnZBTwGPNMgQwmdsNgXGJUlIWEuR5oWUIpz8pAEAMe6TwfwyYsGo/IpFKSAAAh+QQJCgAAACwAAAAAEAAQAAADMwi6IMKQORfjdOe82p4wGccc4CEuQradylesojEMBgsUc2G7sDX3lQGBMLAJibufbSlKAAAh+QQJCgAAACwAAAAAEAAQAAADMgi63P7wCRHZnFVdmgHu2nFwlWCI3WGc3TSWhUFGxTAUkGCbtgENBMJAEJsxgMLWzpEAACH5BAkKAAAALAAAAAAQABAAAAMyCLrc/jDKSatlQtScKdceCAjDII7HcQ4EMTCpyrCuUBjCYRgHVtqlAiB1YhiCnlsRkAAAOwAAAAAAAAAAAA==" />
Register
</div>
</form>
</div>

Angular JS - Control multiple views based on click

There is my router.js
$stateProvider.state("workarea", {
url: "/",
templateUrl: "/templates/workarea.html",
requireLogin: true
}).state("workarea.shared", {
url: "/workarea",
controller: "workareaSharedCtrl",
requireLogin: true,
views: {
"options": {
templateUrl: "/views/options.html"
},
"workspace": {
templateUrl: "/views/workspace.html"
}
}
}).state("workarea.user", {
url: "/:username",
controller: "workareaUserCtrl",
requireLogin: true,
views: {
"options": {
templateUrl: "/views/options.html"
},
"workspace": {
templateUrl: "/views/workspace.html"
},
"comments": {
templateUrl: "/views/comments.html"
}
}
})
This is the /templates/workarea.html
<a ui-sref="workarea.shared">Shared</a>
<a ui-sref="workarea.user">Private</a>
<div ui-view="options" />
<div ui-view="workspace" />
When clicked on Shared, the views (options, workspace and comments) of workarea.shared should be loaded and when clicked on Private the views (options, workspace) of workarea.user should be loaded.
What am I missing here?
There is a working version
There are two issues. Firstly, <div> cannot be self closing, so this is a fix of the parent template
<div>
<a ui-sref="workarea.shared">Shared</a>
<a ui-sref="workarea.user">Private</a>
<!--
<div ui-view="options" />
<div ui-view="workspace" />
-->
<div ui-view="options" ></div>
<div ui-view="workspace" ></div>
</div>
Also, controller belongs to view (even to each of them if more defined) not to state:
...
.state("workarea.shared", {
url: "/workarea",
// NOT here - controller belongs to view
//controller: "workareaSharedCtrl",
requireLogin: true,
views: {
"options": {
templateUrl: "views/options.html",
controller: "workareaSharedCtrl", // here should be definition
},
...
Check it here

Default view in ui-router w/ overwrite option

I have the following state in my app:
state('app', {
url: '',
abstract: true,
templateUrl: 'app/shared/layouts/app.html',
controller: 'appController'
});
The app.html layout have those views:
<header id="topbar" ui-view="top"></header>
<section id="content" ui-view="content"></section>
The "top" view is basically the breadcrumb. But in some features, they are more complex and have a lot of buttons, tabs, etc.
So, in my states, I have:
.state("dashboard", {
parent: 'app',
url: '/dashboard',
views: {
'top': { templateUrl: "app/dashboard/top.html" }
'content': { templateUrl: "app/dashboard/dashboard.html" }
},
controller: 'Dashboard as vm',
data: { requireAuth: true }
});
Is there any way to have a default top template in all states, and only if I want, overwrite this template?
I already tried put the default template into the view:
<header id="topbar" ui-view="top">
<ul class="breadcrumb">[...]</ul>
</header>
Works, but give me a lag between state changes (the default top disapears after the custom top has loaded)
Option 1:
Define the layout in the index (or a parent state) and target the named views at the root state
Plunk 1
index.html:
<body ng-app="plunker">
<a ui-sref='app.child1'>Go to child1</a>
<a ui-sref='app.child2'>Go to child</a>
<h1>Hi from unnamed view in app state</h1>
<div ui-view='header'></div>
<div ui-view='content'></div>
</body>
config:
$stateProvider.state('app', {
url: "",
views: {
"header#": {
template: "<h3>Default header template</h3>"
},
"content#": {
template: "<h5>Default content template</h3>"
}
}
}).state('app.child1', {
url: '/child1',
views: {
"header#": {
template: "<small>Header for child1</small>"
},
"content#": {
template: "<a ui-sref='^'>Back to parent</a><h1>child1</h1>"
}
}
}).state('app.child2', {
url: '/child2',
views: {
"header#": {
template: "<small>Header for child2</small>"
},
"content#": {
template: "<a ui-sref='^'>Back to parent</a><h1>child1</h1>"
}
}
});
Option 2
Define the layout in the app state, as well as the default views to plug into the layout
Plunk 2
index.html:
<body ng-app="plunker">
<div ui-view></div>
</body>
config:
$stateProvider.state('app', {
url: "",
views: {
"header#app": {
template: "<h3>Default header template</h3>"
},
"content#app": {
template: "<h5>Default content template</h3>"
},
"#": {
template: "<a ui-sref='.child1'>Go to child1</a>" +
"<a ui-sref='.child2'>Go to child2</a>" +
"<h1>Hi from unnamed view in app state</h1>" +
"<div ui-view='header'></div>" +
"<div ui-view='content'></div>"
}
}
}).state('app.child1', {
url: '/child1',
views: {
"header#app": {
template: "<small>Header for child1</small>"
},
"content#app": {
template: "<a ui-sref='^'>Back to parent</a><h1>child1</h1>"
}
}
}).state('app.child2', {
url: '/child2',
views: {
"header#app": {
template: "<small>Header for child2</small>"
},
"content#app": {
template: "<a ui-sref='^'>Back to parent</a><h1>child2</h1>"
}
}
});

AngularJS UI-Router default views

Here's the example code.
http://plnkr.co/edit/jXEQ9xemL1A5b9KKKcAw?p=preview
var app = angular.module('npAdmin', ['ui.router']);
app.config(['$httpProvider', '$stateProvider', '$urlRouterProvider',
function($httpProvider, $stateProvider, $urlRouterProvider) {
$stateProvider
.state('common', {
templateUrl: 'tpl.common.html',
abstract: true,
// views: {
// 'footer': {
// templateUrl: 'footer.html'
// }
// }
})
.state('common.dashboard', {
url: '/dashboard',
views: {
'content': {
template: '<div><h4>dashboard</h4></div>'
},
'footer': {
templateUrl: 'footer.html'
}
}
})
.state('common.crm', {
url: '/crm',
views: {
'content': {
template: '<div><h4>CRM</h4></div>'
},
'footer': {
templateUrl: 'footer.html'
}
}
})
.state('common.abc', {
url: '/abc',
views: {
'content': {
template: '<div><h4>ABC</h4></div>'
},
'footer': {
templateUrl: 'newfooter.html'
}
}
})
.state('landing', {
templateUrl: 'tpl.login.html',
abstract: true,
})
.state('landing.login', {
url: '/login',
template: '<div><h4>Wow</h4></div>',
});
$urlRouterProvider.otherwise('/crm');
}
]);
The default 'templateUrl' of 'footer' is 'footer.html', but it's 'newfooter.html' for some state.
Is there a good way to set default footer in this case?
I tried to use 'templateUrl' and 'views' at the same time, but it doesn't work.
There is updated plunker. We can use absolute naming in the parent 'common':
.state('common', {
abstract: true,
views: {
'': {
templateUrl: 'tpl.common.html',
},
'footer#common': {
templateUrl: 'footer.html'
}
}
})
And override it only if needed ('dashboard' and 'crm' will use the parent footer, while the 'abc' is defining an override - special one: newfooter.html)
.state('common.dashboard', {
url: '/dashboard',
views: {
'content': {
template: '<div><h4>dashboard</h4></div>'
},
// provided by parent
//'footer': {
// templateUrl: 'footer.html'
//}
}
})
.state('common.crm', {
url: '/crm',
views: {
'content': {
template: '<div><h4>CRM</h4></div>'
},
// provided by parent
//'footer': {
// templateUrl: 'footer.html'
//}
}
})
.state('common.abc', {
url: '/abc',
views: {
'content': {
template: '<div><h4>ABC</h4></div>'
},
'footer': {
templateUrl: 'newfooter.html'
}
}
Check it here
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#': { }
}
})

Angular UI Router multiple/named routes

I have a user layout file that is the template for any user pages:
<div class="user-wrapper">
<div ui-view="menu"></div>
<div ui-view="content"></div>
</div>
Depending on the state I want the menu to be different. Such as:
.state('user', {
url: '/user',
templateUrl: 'partials/user.html',
controller: 'userController',
})
.state('user.one', {
url: '/one',
controller: 'oneController',
views: {
"menu": { templateUrl: "partials/client-menu.html" },
"content": { templateUrl: "partials/one.html" }
},
});
.state('user.two', {
url: '/two',
controller: 'twoController',
views: {
"menu": { templateUrl: "partials/client-menu.html" },
"content": { templateUrl: "partials/two.html" }
},
});
.state('user.three', {
url: '/three',
controller: 'threeController',
views: {
"menu": { templateUrl: "partials/admin-menu.html" },
"content": { templateUrl: "partials/three.html" }
},
});
Now you can see "one" and "two" both use the same menu but "three" uses a different menu. This all works fine but is there a way to avoid duplicating the menu on "one" and "two".
Such as making a "user.client" state that uses the "user-menu.html" then "one" would be "user.client.one" instead and only have to specify the content.
I think the main problem is the
<div ui-view="content"></div>
is on the grandfather of the "user.client.one" so how can it specify the content?
I would say, that the trick is to move the "menu" view definition into parent state "user"
.state('user', {
url: '/user',
views: {
"" : {
templateUrl: 'partials/user.html',
controller: 'userController',
},
"menu#user": { templateUrl: "partials/client-menu.html" },
},
...
So, what happened? any child state of the "user" will already have the content of the "menu" filled, with the default templateUrl: "partials/client-menu.html"
Any other child, can override that...
.state('user.one', {
url: '/one',
controller: 'oneController',
views: {
// "menu": already set by parent
"content": { templateUrl: "partials/one.html" }
....
.state('user.two', {
url: '/two',
views: {
// "menu": set in parent
"content": { templateUrl: "partials/two.html" }
...
.state('user.three', {
url: '/three',
controller: 'threeController',
views: {
// here we override that
"menu": { templateUrl: "partials/admin-menu.html" },
"content": { templateUrl: "partials/three.html" }
...
Maybe, check this Q & A for some more ideas about multi view nesting:
multiple ui-view html files in ui-router
AngularJS ui-router view structure product site
I think a found a solution user the # for absolute views:
.state('user', {
url: '/user',
templateUrl: 'partials/user.html',
controller: 'userController',
})
.state('user.client', {
url: '/client',
views: {
"menu": { templateUrl: "partials/client-menu.html" }
},
})
.state('user.admin', {
url: '/admin',
views: {
"menu": { templateUrl: "partials/admin-menu.html" }
},
})
.state('user.client.one', {
url: '/one',
controller: 'oneController',
views: {
"content#user": { templateUrl: "partials/one.html" }
},
});
.state('user.client.two', {
url: '/two',
controller: 'twoController',
views: {
"content#user": { templateUrl: "partials/two.html" }
},
});
.state('user.admin.three', {
url: '/three',
controller: 'threeController',
views: {
"content#user": { templateUrl: "partials/three.html" }
},
});
It feels abit cleaner but I'm not sure if its the right approach still.

Resources