I've been working with Angular for a year or 2 now, but this is my first project using ui-router. I'm running into a few issues with views and sub-views. The app is a standard left-side menu bar, with the views on the right changing depending on what's clicked in the menu bar.
On index.html
<body>
<div ui-view></div>
</body>
In the config.js file, which defines the routes
.state("dashboard", {
url: "/dashboard",
templateUrl: "components/dashboard/dashboard.html",
data: {
pageTitle: "Dashboard",
requiresLogin: false
}
})
.state("dashboard.welcome", {
url: "/welcome",
templateUrl: "components/welcome/welcome.html",
data: {
pageTitle: "Welcome",
requiresLogin: false
}
})
In the dashboard.html file
<div class="dashboard">
<div class="container-fluid">
<div class="row">
<div class="col-xs-12 col-md-8">
<div ui-view>
The /dashboard path loads correctly, and will load the left-side navigation bar with a blank right side. But changing the state to dashboard.welcome (/welcome) will not load the welcome.html template.
Whenever working with ui-router you need to understand that the concept of states is different from routes. When you define a sub-state, its defined relative to its parent state. In your scenario dashboard.welcome is defined as a child state of dashboard. The routes to substate is relative to the parent and is {parent url}/{child url}. Hence you should use either of the below 2 to route to that state:
Using $state.go change the state by specifying state name
$state.go('dashboard.welcome');
Using $location.path change the route by specifying url
$location.path('/dashboard/welcome');
It sounds like you want links to /welcome to be for state dashboard.welcome. Here is a plunker showing how this can be done. I show two sets of dashboard and welcome states. The first set of states (dashboard & welcome) shows that /dashboard/welcome will bring you to the dashboard.welcome state.
The second set (dashboard2 & welcome2) shows that /welcome will go to state dashboard2.welcome2. I believe this is what you were looking for.
If you hover over the links you can see where they will take you.
https://plnkr.co/edit/AVKPFa?p=info
Nested routes in ui-router get nested urls. I would however recommend using named-views for this kind of structure. You can find more info about it here:
https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views
The gist of it is: you can specify a named component (ui-view) for your left menu navigation and another one for content, which gives you much more control down the line, because named components can be overwritten in child states or they can keep the default template, depending on your needs.
Example:
.state('root', {
url: '',
abstract: true,
views: {
'header': {
templateUrl: 'templates/partials/header.html',
controller: 'headerCtrl'
},
'logo': {
templateUrl: 'templates/partials/logoView.html'
},
'footer':{
templateUrl: 'templates/partials/footer.html',
controller: 'footerCtrl'
}
}
})
.state('root.login', {
url: '/login',
views: {
'header#': {
template: ''
},
'container#': {
templateUrl: 'templates/login.html',
controller: 'loginController'
}
}
})
.state('root.report', {
url: '/report',
views: {
'container#': {
templateUrl: 'templates/eu_dashboard.html',
controller: 'reportController'
}
}
})
And in your index.html:
<div ui-view="logo"></div>
<div ui-view="header"></div>
<div id="mainView" ui-view="container"></div>
<div ui-view="footer"></div>
Related
I got a sub state that is suppose to load content into a ui-view called ui-solution. It does so while the user is in the flow of creating something called "solution". The strange part is that once that is saved and the user revisit a specific part of the process that calls that same sub state it does not load the content. Can anybody spot anything wrong with this code that could cause this inconsistent behaviour? Just so I can rule this out and start looking elsewhere.
the url change
the url has the same parameters
on reload of the page the right content is loaded in that `ui-view``
The setup of the page is
ui-view
- tabs
-- ui-view="ui-solution"
$stateProvider.state('analyse', {
url: '/analyse/:id',
data: {
solution: false
},
templateUrl: 'app/components/a/a.html',
controller: 'Analisys'
});
$stateProvider.state('analyse.solution', {
url: '/solution',
data: {
solution: true
},
views: {
'ui-solution': {
templateUrl: 'app/components/a/s/s.html',
controller: 'Analisys'
}
}
});
ui-view="ui-solution" is present in a.html one of the tabs there
you try to load nested view and is not in your parent view so it cant loaded - i had the same problem
i suggest to look at this and also the 2 link i sent on the comment
$stateProvider
.state('analyse', {
url: '/analyse/:id',
data: {
solution: false
},
templateUrl: 'app/components/a/a.html',
//controller: 'Analisys' better using ng-controller in the html instead of here
})
.state('analyse.solution', {
url: '/solution',
data: {
solution: true
},
views: {
'ui-solution': {
templateUrl: 'app/components/a/s/s.html'
}
}
});
and your a.html should look like
<div ng-controller="Analisys as ctrl">
<h1 ui-serf="analisys.solution">should load the nested view</h1>
<div ui-view="ui-solution"></div>//here should see the nested html
</div>
and index.html should have
<div ui-view></div>
hope it will help you
btw
you dont need the ui-view="solution" cause its automatically refer to the ui-view inside (unless you have few
so you can do so
$stateProvider
.state('analyse', {
url: '/analyse/:id',
data: {
solution: false
},
templateUrl: 'app/components/a/a.html'
//controller: 'Analisys' better using ng-controller in the html instead of here
})
.state('analyse.solution', {
url: '/solution',
data: {
solution: true
},
templateUrl: 'app/components/a/s/s.html'
});
and a.html
<div ng-controller="Analisys as ctrl">
<h1 ui-serf="analisys.solution">should load the nested view</h1>
<div ui-view></div>//here should see the nested html
</div>
I have a project for which I need 2 layouts. One which is basically 1 column (kind of as a landing page) and one which has a menu at the top, and is basically the same 1 column layout.
I've set up 2 layout HTMLs, which I'm using in my states, but the problem is that every time I switch from one state to the other, the whole HTML in the top-most ui-view (the one in the body) is changed.
Here's my setup:
Routes
.state('root', {
url: '',
abstract: true,
templateUrl: 'app/layouts/logged-in.html'
})
.state('root.homepage', {
url: '/',
templateUrl: 'app/main/main.html',
controller: 'MainController',
controllerAs: 'main'
})
.state('candidate', {
url: '/candidate',
abstract: true,
templateUrl: 'app/layouts/logged-in.html'
})
.state('candidate.profile', {
url: '/profile',
templateUrl: 'app/candidate/profile.html',
controller: 'CandidateProfileController',
controllerAs: 'profile'
})
index.html
<body>
<div ui-view></div>
</body>
logged-in.html
<div>
<my-navbar></my-navbar>
</div>
<div ui-view></div>
logged-out.html
<div ui-view></div>
The problem is that when switching between root.homepage and candidate.profile, my-navbar gets removed and then re-appended, which the user can briefly see (until the new HTML loads). I'd understand if one of the states would have the logged-out.html layout and the other the logged-in.html layout, but we're talking about the same file, and I'm looking to update only the ui-view from inside logged-in.html file.
Might be better to create an abstract state for the logged-in (including the navbar) and then inside introduce div for placing named view mainContent. And configure all your states to extend logged-in state, with defining views:{'mainContent': {controller:... , templateUrl: ... } }
So, it seems I am not getting ui-router, after all.
Here is the broken example:
http://plnkr.co/edit/WgDqTzE3TJjrCZ2pxg5T?p=preview
The actual file structure is:
app/
app.js
index.html
main/
main.html
header/
header.html
footer/
footer.html
sections/
content1/
content1.html
content2/
...
index.html has a simple <div ui-view></div>
main.html has:
<div ui-view="header"></div>
<div ui-view></div>
<div ui-view="footer"></div>
header.html, footer.html, content1.html, ... have actual content.
app.js has:
$stateProvider
.state("app", {
url: "",
abstarct: true,
templateUrl: "main.html"
})
.state("app.main", {
url: "",
abstarct: true,
views: {
"header": {
templateUrl: "header.html"
},
"footer":{
templateUrl: "footer.html"
}
}
})
.state("app.main.content1", {
url: "/",
templateUrl: "content1.html"
});
So, I thought this meant going to "/" would show me header, footer, and automatically insert content in the unnamed ui-view.
It does not. What am I doing wrong?
To make it quickly working (not as intended) I've just added an anchor into the footer and now it is working
<div>
im footer
<div ui-view=""></div>
</div>
Why? because our state def is like this:
.state("app.main.content1", {
url: "/",
templateUrl: "content1.html"
});
Which means:
do search for unnamed view in my parent
The footer view is a parent of the state "app.main.content1", so this way we can target it.
There are other options as well.
Here is what was most likely intended: , state defintion is changed:
.state("app.main.content1", {
url: "/",
views: {
"#app": {
templateUrl: "content1.html"
}
}
So, now we target the app state, its view anchor <div ui-view="">, by the absolute naming. See:
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.
At the end, we can reach the same with even one abstract state and one child, check it here:
$stateProvider
.state("app", {
url: "",
abstarct: true,
views: {
"" : {
templateUrl: "main.html",
},
"header#app": {
templateUrl: "header.html"
},
"footer#app":{
templateUrl: "footer.html"
}
}
})
.state("app.content1", {
url: "/",
templateUrl: "content1.html",
});
Now, the all UI-Router magic is happening in the root state def. We firstly target root (index.html) unnmaed view, to inject the main.html. Next - othe views target this state itself (the main.html) via "header#app" - absolute naming
That would be the most suitable way... working example
I'm trying to find a way for one of my views to have multiple states. Let's say I have this template:
<body>
<div ui-view="overlay">
<div ui-view="content">
</body>
...and these routes:
$stateProvider
.state('base', {
url: '',
abstract: true,
views: {
'overlay': {
templateUrl: '/src/overlay.html'
}
}
})
.state('base.page1', {
url: '/page1/',
views: {
'content#': {
templateUrl: '/src/page1.html'
}
}
})
.state('base.page2', {
url: '/page2/',
views: {
'content#': {
templateUrl: '/src/page2.html',
}
}
});
This allows me to show the "overlay" view on page1 and page2.
The "overlay" view needs to be able to change its state without affecting anything else on the page. I can set up "overlay" as a parent state and add children to it, but I can't figure out a way to activate any of those overlay.child states from inside a base state without losing whatever was in the base state. Can anyone point me in the right direction?
edit: plunker here http://plnkr.co/edit/vPmNhVLZNI2fOAZZOHkg
Your question is not very clear, but maybe what you want is to make them nested?
<div ui-view="parent">
<div ui-view="child">
</div>
</div>
$stateProvider
.state('parent', {
url: "/",
views: {
'parent': {
templateUrl: '/view/parent.html'
}
}
})
.state('parent.child', {
url: "/",
views: {
'child#parent': {
templateUrl: '/view/child.html'
}
}
})
Or is this not what you are looking for?
Edit:
What about this solution
And if you want you can use variables for your current state so that you don't have to define the overlay states for all parent states in your routeProvider like shown in this dirty example:
Edit2:
What you actually want is parallel states. You can read here on the topic and then you find out it is not yet supported in ui-router. But as written in my comment below you can take a look at ng-switch if you don't really need the state variables, since you can create similar parallel behavior with that directive.
Sorry if the title of this is confusing.
I'm converting a template I purchased into an angular.js app.
I want to use different modules to organize the app.
I'm also using version 0.2.5 of angular-ui-router which allows routing with separate modules.
All is well except the template I'm using looks like this:
<div>Global Nav Bar</div>
<div>Content that changes with different states right below Nav Bar</div>
<div class="wrapsContentAndPushesToBottom">
<div>Content that changes with different states at
bottom of page thanks to parent div's class</div>
<div>Global Footer also on bottom of page due
to parent div's class</div>
</div>
I'm having a hard time getting that global footer to work because of that parent wrapping div.
Can someone help me get this to work?
UPDATE:
I can't get suggested ng-include to work with my plunkr example: http://plnkr.co/edit/dgNkHX
I also can't it working using a named view for the footer: http://plnkr.co/edit/BO8NDO
I think you're looking for ng-include. http://docs.angularjs.org/api/ng.directive:ngInclude
That will enable you to extract that global footer out to a separate file and just include it in your template.
<div ng-include src="'globalFooter.tpl.html'"></div>
Try something like this:
.state('root', {
abstract: true,
views: {
'#': {
},
'sideBar#': { templateUrl: 'views/sidebar.html', controller: 'SideBarCtrl' },
'header#': { templateUrl: 'views/header.html' },
'footer#': { templateUrl: 'views/footer.html' }
}
})
.state('main', {
url: '/',
parent: 'root',
views: {
'#': { templateUrl: 'views/main_content.html', controller: 'MainCtrl' }
}
})
This is working for me.. I have a global footer.