Using ui-router in angular, I have setup a 'users' state where all the users in my application are returned in an ng-repeat. I am trying to create the show state for the users profile page however the view of the child state is inheriting the parent view 'templates/users/index.html' (Here is my app.js:
.state('users', {
abstract: true,
name: 'users',
url: '/users',
views: {
nav: {
templateUrl: 'templates/navbar.html',
controller: 'MainCtrl'
},
content: {
templateUrl: 'templates/users/index.html',
controller: 'UsersCtrl'
}
}
})
.state('users.show', {
url: '/show/:id',
views: {
nav: {
templateUrl: 'templates/navbar.html',
controller: 'MainCtrl'
},
content: {
templateUrl: 'templates/users/show.html',
controller: 'UsersCtrl'
}
}
});
Here is my index.html:
<div class="container">
<label>Search: <input ng-model="searchText"></label>
<a ui-sref="users.show({id: user.id})" ng-click="showUser(user.id)" ng-repeat="user in users | filter:searchText ">
<h2> {{ user.name }} </h2>
<h3> {{ user.email }} </h3>
<button ng-click="createConversation({sender_id: current_user.id, recipient_id: user.id})" >Message</button>
</a>
</div>
The url in the browser is http://localhost:8080/#/users/show/2 however the html file being inherited in the users.show state is the parents users (index.html) file.
Any help appreciated!
The nested view users.show will actually render within the user view. So you just need to place another <ui-view> within templates/users/index.html where the show view will render.
Related
I'm trying to define states in a 3 columns layout with the left column having the same content. But at this time, I had to repeat the templateUrl for all direct nested states.
.state('workspace', {
url: '/',
templateUrl: 'app/workspace/workspace.view.html'
})
.state('workspace.images', {
url: 'images',
views: {
'sidebar': {
templateUrl: 'app/workspace/sidebar.view.html',
controller : 'Workspace.SidebarController as vm'
},
'content': {
templateUrl: 'app/workspace/imageslist.view.html',
controller : 'Workspace.ImagesListController as vm'
}
}
})
.state('workspace.images.edit', {
url: '/:key',
templateUrl: 'app/workspace/editor.view.html',
controller : 'Workspace.EditorController as vm'
})
.state('workspace.documents', {
url: 'documents',
views: {
'sidebar': {
templateUrl: 'app/workspace/sidebar.view.html',
controller : 'Workspace.SidebarController as vm'
},
'content': {
templateUrl: 'app/workspace/documentslist.view.html',
controller : 'Workspace.DocumentsListController as vm'
}
}
});
As you can see, I have to repeat the "sidebar" template each time. I would like to be able to configure it form the abstract workspace state.
workspace.view.html contains the "sidebar" and "content" views :
<div class="sidebar" ui-view="sidebar">
<!-- view = sidebar -->
</div>
<div class="content" style="height: 100%" ui-view="content">
<!-- view = content-->
</div>
I think I should have a views object into the "workspace" state to define the "sidebar" templates and controllers and leave the "content" empty.
And have tried with and without named views but without success. When I do that, the sidebar template is never displayed but seems to be loaded (a wrong name cause a 404 in my console).
This will inject sidebar into parent... and child can use it or even change it:
.state('workspace', {
url: '/',
views: {
'': { templateUrl: 'app/workspace/workspace.view.html' }
'sidebar#workspace': {
templateUrl: 'app/workspace/sidebar.view.html',
controller : 'Workspace.SidebarController as vm'
},
}
})
.state('workspace.images', {
url: 'images',
views: {
'content': {
templateUrl: 'app/workspace/imageslist.view.html',
controller : 'Workspace.ImagesListController as vm'
}
}
})
...
We used views : {} even on parent state. The default unnamed is the original workspace.view.html
We also added named view 'sidebar#workspace' - the absolute name here means, that the name is 'sidebar' and its target is searched inside of the 'workspace' state templates
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'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'