I have a working application with everything working with a structure like this:
<div class="container">
<div class="row">
<div class="col-md-2">
<!-- alot of repeated html used by all the states here -->
</div>
<div class="col-md-8">
<!-- the unique html specific to the state goes here -->
</div>
</div>
</div>
and router config like this
$stateProvider
.state('dashboard', {
url: '/dashboard',
templateUrl: './partials/dashboard.html'
})
.state('applications', {
url: '/applications',
templateUrl: './partials/applications.html'
})
.state('server', {
url: '/server',
templateUrl: './partials/server.html'
})
The problem is that here is a lot of repetition of html code.
How do I make an interface that all states inherit from so that I concentrate on uniques html of a state.
You can use abstract state, f.e. called base:
.state('base', {
url: '',
templateUrl: 'path/to/layout.html', // here you should declare named ui-views, f.e. named content
abstract: true
})
.state('base.dashboard', {
url: '/dashboard',
views: {
content: {
templateUrl: "/path/to/dashboard/content.html"
}
}
})
// the rest of the states goes the same below.
...
Hope that helped somehow.
For more info please refer here.
Related
I am having trouble working out how to structure my routing code with UI router.
In my index file, I call in the menu template and 2 sidebar templates as follows:
index.html
<!-- head code above here -->
<body ng-app="ICP_App" ng-cloak>
<div layout-fill layout="row" ng-controller="AppController as AppCtrl">
<div ui-view="left-nav"></div>
<div ui-view="right-nav"></div>
<div ui-view="toolbar"></div>
<!-- this is where I am expecting/wanting my dashboard template to load (and others when at the correct url-->
<div class="page-content view" ui-view></div>
<!-- footer code under here -->
My routes:
$urlRouterProvider.otherwise('dashboard');
$stateProvider
.state('root', {
abstract: true,
templateUrl: '../partials/icp_index.html',
controller: 'AppController as AppCtrl',
url: '',
views: {
'left-nav': {
templateUrl: '../partials/left-nav.html'
},
'right-nav': {
templateUrl: '../partials/right-nav.html'
},
'toolbar': {
templateUrl: '../partials/toolbar.html'
}
}
})
.state('root.dashboard', {
url: '/dashboard',
templateUrl: '../partials/agency-dashboard.html',
controller: 'AppController as AppCtrl'
})
However, the menu and sidebars load, but the main content area is left blank. I wonder if it is to do with the '.otherwise'?
My index file should contain the menu and sidebars, then the content for that state to be loaded where I have indicated above - in this case the dashboard template. The '.otherwise' is there to show the dashboard (within the index file) when a url is incorrect or incomplete.
Thanks in advance.
You have to name that ui-view, and on your child state, you need to specify and/or override the desired view, so it would be something like:
<div class="page-content view" ui-view="main-content"></div>
and the state definition:
.state('root.dashboard', {
url: '/dashboard',
views: {
'main-content#root': {
templateUrl: '../partials/agency-dashboard.html',
controller: 'AppController as AppCtrl'
}
}
})
Also you should have two templates, one only defines the root ui-view (index.html), and the other one is your icp_index which contains the named views. This plunkr shows the structure:
plnkr.co/edit/zjngTr0JU6cUGBtMsfN0
I have the following state declared:
.state('select', {
url: '/select/:youtubeId',
views: {
'': {templateUrl: 'partials/select/select.html'},
video: {
templateUrl: 'partials/select/select.video.html',
controller: 'RecordVideoController'
},
setParts: {
templateUrl: 'partials/select/select.set-parts.html',
controller: 'RecordPartsController'
},
listParts: {
templateUrl: 'partials/select/select.list-parts.html',
controller: 'RecordPartsController'
}
}
})
All template files exist, this is for example, partials/select/select.html:
<div>
<div class="row">
<div ui-view="video" class="col-sm-9"></div>
<div class="col-md-3 text-center">
<div class="row">
<div ng-class="{'col-md-12': parts.length == 0, 'col-md-6': parts.length !== 0}" ui-view="setParts"></div>
<div ng-show="parts.length" ui-view="listParts" class="col-md-6"></div>
</div>
</div>
</div>
<div ui-view="ui-view"></div>
</div>
So, as it can be seen it only define the layout in order to set the proper state views.
Them problem is that none of the other views is being rendered, neither the corresponding controllers is being instantiated.
How is it supposed to work?
can you provide total code , then i can check your states declared correct or no i am also thinking that your not using nested states correctly
[enter link description here][1] check below link
Angular ui router multiple named views for all states
.state('select', {
url: '/select/:youtubeId',
views: {
'': {templateUrl: 'partials/select/select.html'},
//viewName#stateName
'video#select': {
templateUrl: 'partials/select/select.video.html',
controller: 'RecordVideoController'
},
'setParts#select': {
templateUrl: 'partials/select/select.set-parts.html',
controller: 'RecordPartsController'
},
'listParts#select': {
templateUrl: 'partials/select/select.list-parts.html',
controller: 'RecordPartsController'
}
}
})
try like this ..
I don't think you're nesting your views correctly. It's been a while since I worked with ui-router, but I think your nested states should look something like
video#select: {
templateUrl: 'partials/select/select.video.html',
controller: 'RecordVideoController'
},
Also, I'm not sure what you're trying to do with the <div ui-view="ui-view"></div> line - I think that can just be <div ui-view></div> if you're trying to render the default view there, or you need to specify which view you want as you did above.
I'm try to use ui-router to manage nested views on a single page app.
Let's say I want to create a dashboard application with a common area and multiple views.
The main and the nested states are handled like this:
$stateProvider.state('home', {
url: '/',
template: 'MY HOME PAGE'
})
.state('login', {
url: '/login',
templateUrl: '/pages/login.html'
})
.state('registration', {
url: '/registration',
templateUrl: '/pages/registration.html'
})
.state('dashboard', {
url: '/dashboard',
templateUrl: '/pages/dashboard/dashboard.html'
})
.state('dashboard.me', {
url: '/me',
templateUrl: '/pages/dashboard/me.html'
})
.state('dashboard.messages', {
url: '/messages',
templateUrl: '/pages/dashboard/messages.html'
})
.state('dashboard.friends', {
url: '/friends',
templateUrl: '/pages/dashboard/friends.html'
});
The dashboard HTML page is the following:
<div class="container" ng-controller="dashboardCtrl" ng-init="init()">
<h2>DASHBOARD</h2>
<ul>
<li><a ui-sref="dashboard.me">Me</a></li>
<li><a ui-sref="dashboard.messages">My Messages</a></li>
<li><a ui-sref="dashboard.friends">My Friends</a></li>
</ul>
<div ui-view></div>
The above HTML is also included inside an ui-view.
Everything works fine if I navigate my application using the anchors.
By the way if I try to go directly to myhost/dashboard/me or myhost/dashboard/friends (every path with two levels of nested views) the app doesn't work. I get an angular (unexpected token <) but I don't think it's relevant...
It seems like it's not able to resolve the first level of nested view.
The following images show the HTML obtained when the navigation is done using anchors:
and the HTML obtained when the page is called directly from the browser address link:
Any ideas? Thanks.
The problem is that angular is not loading, hence the error. The code looks correct to me though, unless you missed the closing tag for
<div class="container" ng-controller="dashboardCtrl" ng-init="init()">
?
JavaScript State Configurations :
$stateProvider.state('home', {
url: '/',
template: 'MY HOME PAGE'
})
.state('login', {
url: '/login',
templateUrl: '/pages/login.html'
})
.state('registration', {
url: '/registration',
templateUrl: '/pages/registration.html'
})
.state('dashboard', {
url: '/dashboard',
abstract: true,
parent: 'home',
templateUrl: '/pages/dashboard/dashboard.html'
})
.state('dashboard.me', {
url: '/me',
views: {
'#dashboard_view': {
templateUrl: '/pages/dashboard/me.html'
}
}
})
.state('dashboard.messages', {
url: '/messages',
views: {
'#dashboard_view': {
templateUrl: '/pages/dashboard/messages.html'
}
}
})
.state('dashboard.friends', {
url: '/friends',
views: {
'#dashboard_view': {
templateUrl: '/pages/dashboard/friends.html'
}
}
});
Dashboard HTML
<div class="container" ng-controller="dashboardCtrl" ng-init="init()">
<h2>DASHBOARD</h2>
<ul>
<li><a ui-sref="dashboard.me">Me</a></li>
<li><a ui-sref="dashboard.messages">My Messages</a></li>
<li><a ui-sref="dashboard.friends">My Friends</a></li>
</ul>
<div ui-view="dashboard_view"></div>
You can get more details:
Multiple Named Views
Nested States and Nested Views
I have a problem when whorking on Ionic with angularJs, the problem is in routing system when I try to develop a login page .
In the controller part of code i'l trying to load a welcome page calle 'dash' with state.go(psc.dash)
here is my controller.js :
angular.module('starter.controllers', [])
.controller('LoginCtrl', function($location, $state) {
var user = this;
user.login = function() {
console.log("LOGIN user: " + user.email + " - PW: " + user.password);
setTimeout(function() {
state.go('psc.dash');
}, 20);
};
})
.controller('DashCtrl', function($scope, $location) {});
here is my App.js:
.config(function($stateProvider, $urlRouterProvider, $locationProvider) {
$stateProvider
.state('login', {
url: "/login",
views: {
'login': {
templateUrl: "templates/login.html",
controller: "LoginCtrl"
}
}
})
.state('psc', {
url: "/psc",
abstract: true,
templateUrl: "templates/psc.html"
})
.state('psc.dash', {
url: "/dash",
views: {
'psc-dash': {
templateUrl: "templates/dash.html",
controller: "DashCtrl"
}
}
})
;
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/login');
});
and here is my login.html
<div class="list list col span_1_of_2 " ng-controller="LoginCtrl as loginCtrl">
<label class="item item-input">
<span class="input-label">E-mail</span>
<input type="text" ng-model="loginCtrl.email">
</label>
<label class="item item-input">
<span class="input-label">password</span>
<input type="password" ng-model="loginCtrl.password">
</label>
<div>
<div class="col span_2_of_3">forgot password ? </div>
<button class="button button-positive col span_1_of_3" ng-click="loginCtrl.login()">
connect
</button>
</div>
</div>
The problem is when I click on connect button the url '/psc/dash' appears in address bar but the login view stay displayed and the page is not reloaded with the new html view.
EDIT
This is wrong. There is a discrepancy in the ui-router documentation: states that inherit from an abstract state are prefixed with their parents URL.
The reason is that although your 'psc.dash' nested state is defined as a child of the 'psc' state, the URL you have assigned to it is not automatically prefixed with its parent's URL.
You want to change the 'psc.dash' state definition to this:
.state('psc.dash', {
url: "/psc/dash",
views: {
'psc-dash': {
templateUrl: "templates/dash.html",
controller: "DashCtrl"
}
}
})
Take a look at the ui-router documentation for why this is:
What Do Child States Inherit From Parent States?
Child states DO inherit the following from parent states:
Resolved dependencies via resolve Custom data properties
Custom data properties
Nothing else is inherited (no controllers, templates, url, etc).
The service is $state so the code should be:
$state.go('psc.dash');
You can get rid of the controller definition in your HTML:
<div class="list list col span_1_of_2" ng-controller="LoginCtrl as loginCtrl">
</div>
use this instead:
<div class="list list col span_1_of_2">
</div>
and change the state this way:
.state('login', {
url: "/login",
views: {
'login': {
templateUrl: "templates/login.html",
controller: "LoginCtrl as loginCtrl"
}
}
})
You don't need to define the controller twice.
Your login.html template does not use <ion-view>. It would be interesting to see a plunker of your project.
Another things I am not sure about is the state definition. If the view login is not wrapped in another container you should write it like this:
.state('login', {
url: "/login",
templateUrl: "templates/login.html",
controller: "LoginCtrl as loginCtrl"
})
SOLUTION:
my psc route should be named 'login' to be identified
.state('psc', {
url: "/psc",
views: {
'login': {
abstract: true,
templateUrl: "templates/psc.html"
}
}
})
edit: Based on the answer by #actor2019 I want to update my question to better explain the problem:
Using Angular UI-Router(v0.0.2), I've setup the app to properly navigate between main "pages"/state, while inheriting the base state.
Index.html:
<div ui-view></div>
base.html:
<!-- Header -->
<div>
<!-- Header markup -->
<!-- Search View -->
<div ui-view="search"></div>
</div>
<!-- Page Content view -->
<div ui-view></div>
The issue is here in the app.js file. When I add the views parameter to the base state, everything stops working(100% blank page). Without that parameter, the page renders correctly, but I have no search view.
app.js:
$urlRouterProvider.otherwise('/');
//
// Now set up the states
$stateProvider
.state('base', {
abstract: true,
templateUrl: 'views/base.html',
views: {
"search": {
templateUrl: "views/search.html"
}
}
})
.state('base.home', {
url: "/",
templateUrl: "views/home.html"
})
.state('base.page2', {
url: "/page2",
templateUrl: "views/page2.html"
});
How do I add views to this parent 'base' state?
UPDATE:
The problem with #actor2019's answer here is that the search view gets reinitialized when the state changes. I'd like the views off the base level to persist through state changes.
The first obvious mistake:
You can't specify controller and template on the state while your using views. They are mutually exclusive...
This is because when there is no "views" but a controller and template on the state, UI-Router automatically creates the "views" property and pulls those properties to an "empty" view...
.state('base', {
abstract: true,
templateUrl: 'views/base.html', //Can't do this
views: { // when this is there.
"search": {
templateUrl: "views/search.html"
}
}
})
Instead do:
.state('base', {
abstract: true,
views: {
"": {
templateUrl: 'views/base.html'
},
"search": {
templateUrl: "views/search.html"
}
}
})
Second problem:
How views targeting works with nested views etc. is not very logical, it may work well if you restrict your self to one view in one view all the way down, but ones you start working with multiple named views it all gets confusing... Add unnamed views on top and many people gets lost...
The way views work in UI-Router is the worst part of UI-Router...
Given you example I am not even entirely sure of the way to target the search view from your abstract parent state... Might be:
.state('base', {
abstract: true,
views: {
"": {
templateUrl: 'views/base.html'
},
"search#base": {
templateUrl: "views/search.html"
}
}
})
If it can even be made to work... Alternatively you can move the search view out of base.html, but I guess you added it in there for a reason.
The whole view concept is the biggest reason why I ended up writing https://github.com/dotJEM/angular-routing instead.
The Child state should be home.search instead of header.search. In your case, you may want to write some abstract state to hold the layout,
base.html
<div class="row-fluid">
<div class="header">
<div class="span3" ui-view="logo"></div>
<div class="span9" ui-view="menu"></div>
</div>
</div>
<div class="row-fluid">
<div class="content">
<div class="span2" ui-view="sidebar"></div>
<div class="span10" ui-view="entry"></div>
</div>
</div>
in app.js
$stateProvider
.state('base',{
abstract:true,
url:'/',
templateUrl: viewBase+'base.html'
})
.state('base.main',{
url:'',
views:{
"logo":{
templateUrl:viewBase+'main/logo.html'
},
"menu":{
templateUrl:viewBase+'main/menu.html'
},
"sidebar":{
templateUrl:viewBase+'main/sidebar.html'
},
"entry":{
templateUrl: viewBase+'main/entry.html'
}
}})
According to the ui-router documentation, when the application is in a particular state—when a state is "active"—all of its ancestor states are implicitly active as well. So, for example, when the "contacts.list" state is active, the "contacts" state is implicitly active as well, because it's the parent state to "contacts.list". Child states will load their templates into their parent's ui-view. I'd reccomend looking over the section of their documentation entitled Nested States & Views to gain a fuller understanding of how to do this.
In the code you have provided us here, the parent state of the search template is home, while
.state('header.search', {
templateUrl: "views/search.html",
controller: "SearchCtrl"
})
implies that the parent state of the search template should be header in order for the view to get loaded correctly. So, I believe the following changes to your app.js will fix your issue.
app.js
$stateProvider
.state('home', {
url: "/",
views: {
'': {
templateUrl: "views/mainContent.html",
controller: "MainCtrl"
},
'header': {
templateUrl: "views/header.html"
},
'footer': {
templateUrl: "views/footer.html"
},
}
})
.state('home.search', {
views: {
'search': {
templateUrl: "views/search.html",
controller: "SearchCtrl"
}
})
.state('anotherPage', {
url: "/anotherPage",
templateUrl: "views/anotherPage.html"
});
This works for me.
$stateProvider
.state('base', {
abstract: true,
url:'/',
templateUrl: 'views/base.html'
})
.state('base.home', {
url: "",
views: {
"search#base": {
templateUrl: "views/searchOfHome.html"
}
//content#base, contentOfHome.html
}
})
.state('base.page2', {
url: "page2",
views: {
"search#base": {
templateUrl: "views/searchOfPage2.html"
}
//content#base, contentOfPage2.html
});
If 'base' is the root state, you don't need the '#base'