I want to use ui-view and ui-router in a part of my page.But for some reason I can't get to show up my template "app_usermng_table.html".
The main code of this part is as follows:
1.router config part
(I have add the "ui.router" dependency in my module "app" yet.)
.state('app.usermng', {
url: '/usermng',
templateUrl: 'tpl/app_usermng.html',
resolve: {
deps: ['uiLoad',
function( uiLoad ){
return uiLoad.load( ['js/controllers/usermng/usermng.js','css/usermng.css'] );
}]
},
onEnter: function(){
console.log("enter usermng");
}
})
.state('app.usermng.table',{
url:'/table',
templateUrl:'tpl/app_usermng_table.html',
resolve:{
deps:[
'uiload',
function(uiload){
return uiload.load(['js/controllers/usermng/usermngtable.js'])
}
]
},
onEnter: function(){
console.log("enter usermng table");
}
})
2.app_usermng.html:
I want to put the template in the
<div ui-view></div>
tag.
<div class="hbox hbox-auto-xs hbox-auto-sm" ng-controller="UsermngCtrl">
<div ng-controller="UsermngCtrl">
<div class="bg-light lter b-b wrapper-md">
<h1 class="m-n font-thin h3">User Management</h1>
</div>
<div ui-view></div>
</div>
</div>
3.usermng controller
I add the code
$state.transitionTo
hoping when I enter the state "app.usermng", the state "app.usermng.table" can be shown in its parent.
app.controller('UsermngCtrl',['$scope','$state',function($scope,$state){
$state.transitionTo('app.usermng.table');
console.log("table")
}]);
4.navigation bar
I hope it jumps to the state "app.usermng" when I click 'UserManagement' on the navigation bar.
<li ui-sref-active="active">
<a ui-sref="app.usermng">
<i class="glyphicon glyphicon-list-alt icon text-info-dker"></i>
<span class="font-bold" translate="aside.nav.usermng">User Management</span>
</a>
</li>
However,all I've done seems to be useless...The table part isn't shown.The console content is as follows:
enter usermng
usermng.js:3 table
usermng.js:3 table
It seems that my child state "app.usermng.table" never enters.
Could anyone tell me what's the problem.Thanks a lot! :)
You need to add an 'abstract':true property, like here:
.state('app.usermng', {
url: '/usermng',
templateUrl: 'tpl/app_usermng.html',
abstract: true,
resolve: {
deps: ['uiLoad',
function( uiLoad ){
return uiLoad.load( ['js/controllers/usermng/usermng.js','css/usermng.css'] );
}]
},
onEnter: function(){
console.log("enter usermng");
}
})
.state('app.usermng.table',{
url:'/table',
templateUrl:'tpl/app_usermng_table.html',
resolve:{
deps:[
'uiload',
function(uiload){
return uiload.load(['js/controllers/usermng/usermngtable.js'])
}
]
},
onEnter: function(){
console.log("enter usermng table");
}
})
It's hard to tell what you are going for here. You are giving the table state a URL which makes me think you are wanting this to be a standalone state, in which case you need to just transitionTo that state directly instead of transitioning to the parent state. If the table state is just meant to be a piece of the parent state, there is a way to do that.
You need to use named views. If I am correct that app.usermng.table isn't a state in it's own right, it's a state that only exists to be shown as part of the app.usermng state. Therefore, when you define app.usermng, you need to declare the table as a named view.
$stateProvider
.state('app.usermng', {
views: {
'table': {
templateUrl:'tpl/app_usermng_table.html',
resolve: []
}
}
})
Then, in the parent template, in the div where you want to insert the table, you should declare it like <div ui-view="table"></div>
See here: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
Related
I'm trying to create a state which acts like a popup, i.e it doesn't clear the current state, it just pops over it without completely destroying the current state (So that the user can gain access to it by dismissing the popup).
Heavily simplified, my applications routes is something like the following:
angular.module('test', ['ui.router'])
.config(['$stateProvider', '$urlRouterProvider',
function($stateProvider, $urlRouterProvider) {
$stateProvider
.state('login', {
url: '/login',
template: '<button><a ui-sref="authenticated.home">Login</a></button>'
})
.state('authenticated', {
url: '/authenticated',
template: '<p>We are now authenticated</p>' +
'<a ui-sref="authenticated.home">home</a>' +
'<a ui-sref="authenticated.statistics">statistics</a>' +
'<a ui-sref="authenticated.popup">Popup!</a>' +
'<div ui-view></div>' +
'<div ui-view="popup"></div>'
})
.state('authenticated.home', {
url: '^/home',
template: '<p>We are in home. <br><input></p>'
})
.state('authenticated.statistics', {
url: '^/statistics',
template: '<p>We are in statistics. <br><input></p>'
})
.state('authenticated.popup', {
url: '^/popup',
views: {
popup: {
template: '<div id="popup"><p>popup is up</p>' +
'<a ui-sref="authenticated.home">close</a>' +
'</div>'
}
}
});
$urlRouterProvider.otherwise('/login');
}
]);
a {
margin-right: 20px;
text-decoration: none;
}
#popup {
position: absolute;
top: 0;
bottom: 0;
width: 100%;
background: #000;
color: #fff;
}
<script src="//cdnjs.cloudflare.com/ajax/libs/angular-ui-router/0.2.18/angular-ui-router.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/angular.js/1.4.8/angular.js"></script>
<div ng-app="test">
<div ui-view>
</div>
</div>
User is presented with a login screen
Once logged in, user is redirected to authenticated.home state. The authenticated parent state holds a navigation menu and <ui-view> to attach subviews
User can use this navigation to navigate around the application to other routes like authenticated.statistics, authenticated.popup.
The problem is that, when I simply move to the popup state, even though I have specified popup view inside it's views object, it clears the other ui-view (makes sense though, because we're no longer in a state that matches it).
One solution I can think of is to use something like ui-router-extras
to go back to previous state, the problem with this is that any changes the user might have been making in the previous states will be lost.
Another solution will be to have the template of popup in the authenticated states template and show/hide it. But the problem with this is that, the popup should be a bookmark-able state, which loads data from server based on state params.
Is there a better approach to create a state that acts like a popup over current state? maybe by changing the template structure or using something like abstract-states that I haven't thought of?
The sticky states add on from ui-router-extras should be what you're looking for. Gives you the ability to create sticky/parallel states so it should allow you to create a popup state without affecting the original state you were in.
I haven't experimented enough with it to know all the details but the main idea is to move all you main states under a root app state and set sticky to true:
$stateProvider.state('app', {
url: '',
views: {
'app': {
templateUrl: 'app.html',
controller: 'mainCtrl',
}
},
sticky: true
});
$stateProvider.state('app.account', {
url: '/account',
templateUrl: 'account.html'
});
$stateProvider.state('app.account.stuff', {
url: '/stuff',
template: "<h3>Here's my stuff:</h3><ul><li>stuff 1</li><li>stuff 2</li><li>stuff 3</li></ul>"
});
After that add your modal state as a sibling (so not as a child of the root app state)
$stateProvider.state('modal', {
url: '/modal',
views: {
'modal': {
templateUrl: 'modal.html'
}
}
});
I took the example provided in the docs and made a few edits to add controllers and simplify it: http://plnkr.co/edit/4JGdIcDUQs0Za4fBaLj1?p=preview
sounds like something abstract states could fix.
changing your "authenticated" state to an abstract parent and attaching a controller to it can allow you to axx data being transformed by child states. Also you could just make a separate controller for your data and drop an ng-controller on the body tag.
If you're still having probs implementing, how about you use LocalStorage to persist the data you want?
Update2 Found a post and changed the plnk on updating ng-model as you type, but i see why OP wants a popup.
Edit
Plnkr Shared State Example
I'll try to find a good example, but here's some pseudo code for now.
.state('authenticated', {
abstract: true,
url: '/authenticated',
controller: 'AuthStateController',
template: '<p>We are now authenticated</p>' +
'<a ui-sref="authenticated.home">home</a>' +
'<a ui-sref="authenticated.statistics">statistics</a>' +
'<a ui-sref="authenticated.popup">Popup!</a>' +
'<div ui-view></div>' +
'<div ui-view="popup"></div>'
})
I believe with or without {abstract: true} your popup state should have access to data inside of 'AuthStateController'. The resolve stuff may be an overkill solution, but might give you an idea of how to use the controllers data more efficiently.. if all else fails .. build a service to handle the data.
.state('authenticated.popup', {
url: '^/popup',
views: {
popup: {
resolve: {
ctrl: 'AuthStateController',
data: function(ctrl) {
return ctrl.data_for_popup;
}
}
template: '<div id="popup"><p>popup is up</p>' +
'<a ui-sref="authenticated.home">close</a>' +
'</div>'
}
}
})
Probably what you are looking for is a different on your main page.
You need to keep 2 ui-view on your index.html or your base template.
And then while loading the states do something like:
$stateProvider.state('stateNAme', {
url: '/name',
views: {
"main": {
controller: '1Controller',
templateUrl: 'tpl1.html'
},
"sidebar#": {
controller: '2Controller',
templateUrl: 'tpl2.html'
}
}
Read here about the multiple named views
I want to have a nested view with UI router, which I've done before (see image) with a main section and then a nav which loads sub-sections into the nested UI-View. This I can do, no issues.
My question is: this time I need to have the initial child state not show to the user until a button is clicked, like this:
Can I do this? Or is it better to load the "baseball" view but hide it and the nav with ng-hide?
UPDATE
Someone asked how I would do the simple nested states in a case like this:
(function() {
'use strict';
angular.module('elements').config(function($stateProvider) {
$stateProvider.state('elements', {
url: '/elements',
abstract: true,
templateUrl: 'modules/elements/templates/elements.html',
controller: 'ElementsController as elements'
})
.state('elements.buttons', {
url: '/elements/buttons',
templateUrl: 'modules/elements/templates/elements-buttons.html'
})
.state('elements.accordion', {
url: '/elements/accordion',
templateUrl: 'modules/elements/templates/elements-accordion.html',
controller: 'AccordionController as accordion'
})
.state('elements.colorcharts', {
url: '/elements/colorcharts',
templateUrl: 'modules/elements/templates/elements-colors-charts.html',
controller: 'ChartColorsController as charts'
})
.state('elements.grid', {
url: '/elements/grid',
templateUrl: 'modules/elements/templates/elements-grid.html'
});
});
})();
Yes it is absolutely possible. I usually accomplish this by using programmatically defined states, which looks like it should work for your situation.
If you have a state for baseball then you could control it as such:
state config
.state('baseball', {
url: '/views/baseball',
template: 'imabaseball!'
})
html
<div ui-view="{{state}}">
<button ng-click="state = 'baseball'">Show Baseball</button>
Then the view in question would not be rendered until the user clicked the button
I have a simple angularjs controller which uses jquery that logs something to the console when mouse goes over an anchor element:
app.controller('MenuController', function() {
$("a").on('mouseover', function (e) {
console.log("mouser over a link");
});
});
I am using ui-router for organizing my app states:
app.config(["$urlRouterProvider", "$stateProvider", function($urlRouterProvider, $stateProvider) {
// For any unmatched url, redirect
$urlRouterProvider
.otherwise("/");
$stateProvider
.state('menu', {
controller: "MenuController",
controllerAs: "menuCtrl",
templateUrl: "partials/menu.html"
})
.state('menu.menu', {
url: '/',
templateUrl: "partials/menu.menu.html"
})
.state('menu.difficulty', {
url: '/difficulty',
templateUrl: "partials/menu.difficulty.html",
controller: "DifficultyController",
controllerAs: "difCtrl"
})
.state('menu.settings', {
url: "/settings",
templateUrl: "partials/menu.settings.html"
})
}]);
My basic html for the menu is in the menu.html file:
<!-- view - menu -->
<div ui-view>
<!-- nested views -->
</div>
Inside here a bunch of nested views get inserted through states. These views have a lot of anchor elements yet nothing happens when mouse goes over them. Why is that so? Shouldn't parent state controller expand on to child states? Thanks for the help!
The data which is bound to $scope in parent controller is accessible in the child states because the parent controller always runs if we access the child state.
In Menu Controller,If write write
$scope.name = 'XYZ'
This $scope.name is accessible in every child controller using $scope.name.
EDIT:
In your MenuController bind this anchor on document like this and It will work
app.controller('MenuController', function() {
$(document).on('mouseover','a', function (e) {
console.log("mouser over a link");
});
});
I have a parent state with data that I would like to use for making child states with dynamic URL params. Currently, I am getting this error, "Error: Could not resolve 'authors/view/Baxter Richard' from state 'dash/authors'".
In my router.js file:
.state('dashboard', {
url: "/auth/dashboard",
templateUrl: 'angular/dashboard.html',
controller: 'DashboardController',
resolve: {
user : function(UserService) {
return UserService.get();
},
user_drive : function(UserDriveService) {
return UserDriveService.get();
}
}
})
.state('dash/authors', {
parent: 'dashboard',
url: "/authors",
templateUrl: 'angular/partials/dash.authors.html'
})
.state('authors/create', {
parent: 'dash/authors',
url: "/create",
templateUrl: 'angular/partials/dash.authors.create.html'
})
.state('authors/view', {
parent: 'dash/authors',
url: "/:title",
templateUrl: 'angular/partials/dash.author-view.html'
})
.state('dash/epubs', {
parent: 'dashboard',
url: "/epubs",
templateUrl: 'angular/partials/dash.epubs.html'
});
$urlRouterProvider.otherwise('/');
$locationProvider.html5Mode(true).hashPrefix('!');
And in my dashboard_controller.js file:
angular.module("app").controller("DashboardController", function($scope, $location, $http, user, user_drive) {
$scope.user = user.data;
$scope.user_drive = user_drive.data;
});
And in my dash.authors.html partial:
<div class="col-sm-3" ng-repeat="author in user_drive.authors | orderBy:'title'">
<a ui-sref="authors/{{author.title}}">
<div ng-repeat="img in user_drive.author_imgs | filter: { parent_id: author.id }">
<img src="{{ img.img_src }}" alt="{{author.title}}" style="height:50px; border: 1px solid black;">
</div>
</a>
<div>
So, when I click on the anchor tag with the ui-sref of "authors/{{author.title}}", I get that error.
I ended up renaming my states using the dot-style syntax. AFAIK, the problem was that I wasn't actually prepending the child state name with its exact parent state name (eg: 'dashboard' and 'dashboard.author') because I thought I just had to set the parent property inside the state (eg: "parent: 'dashboard',") and just name the child state whatever I wanted.
I'm not sure whether you understand the concept of states in angular-ui-router. I suggest you thoroughly read the docs first. The state name is not the same as the state url. The correct usage would be for example:
<a ui-sref="authors/view({title:author.title})">...</a>
It passes the author.title variable to the state as title, meaning it replaces :title in the url. I also recommend not using slashes in the state name, as it seems very confusing.
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'