I followed this example to set up a page with 2 child views.
I have everything in place now, and if I click on the specific link, the subviews are displayed as expected.
Here's my app.js file, in which you can see all of my states:
$stateProvider.state('app',
name: 'app'
url: '/app'
abstract: true
templateUrl: './sections/menu/menu.tpl.html'
).state('app.home',
name: 'home'
url: '/home'
templateUrl: './sections/Home/Home.tpl.html'
).state('app.details',
name: 'appDetails'
url: '/details/:zoneID'
templateUrl: './sections/zoneDetails/zoneDetails.tpl.html'
controller: 'currentZoneFilter'
).state('app.details.overview',
name: 'appDetailsOverview'
url: '/details/:zoneID'
templateUrl: './sections/zoneDetails/zoneDetailsOverview.tpl.html'
).state('app.details.edit',
name: 'appDetailsEdit'
url: '/details/edit/:zoneID'
templateUrl: './sections/zoneDetails/zoneDetailsEdit.tpl.html'
).state('app.setup',
name: 'setup'
url: '/setup'
templateUrl: './sections/setup/setup.tpl.html'
).state 'app.about',
name: 'about'
url: '/about'
templateUrl: './sections/about/about.tpl.html'
controller: 'info'
$urlRouterProvider.otherwise 'app/home'
as you can see, I have app.details, app.details.overview, app.details.edit.
app.details is the parent, and here's the page code:
This it the parent page
<a ui-sref="app.details.edit">Show edit</a>
<a ui-sref="app.details.overview">Show overview</a>
<div ui-view></div>
If I click on the link, the right template and page section is displayed. My question here is: How can I have Overview loaded by default when I reach this page?
I had a look at $urlRouterProvider.when, that I think that .when is good if you have a different URL.
My URL should be details/:zoneID for the page with overview template loaded and details/edit/:zoneID when the edit template is loaded, so I think that .when is not a good approach. Any help? thanks
You have to use the rule function of $urlRouterProvider.
$urlRouterProvider.rule(function ($injector, $location) {
// check if the location is the desired location then move to the //new location.
});
At the end, I finally solved this.
I changed state URLs for app.details / app.details.overview / app.details.edit like this:
.state('app.details',
name: 'appDetails'
url: '/:zoneID'
templateUrl: './sections/zoneDetails/zoneDetails.tpl.html'
controller: 'currentZoneFilter'
.state('app.details.overview',
name: 'appDetailsOverview'
url: '/details'
templateUrl: './sections/zoneDetails/zoneDetailsOverview.tpl.html'
.state('app.details.edit',
name: 'appDetailsEdit'
url: '/edit/day:day'
templateUrl: './sections/zoneDetails/zoneDetailsEdit.tpl.html'
so that now the URL starts with the zoneID and then it appends the details or edit to display the right template.
Here are a couple way. Check https://github.com/angular-ui/ui-router/wiki for implementation details.
Listen for the $StateChangeStart event. If this matches your state, perform a redirect. Be sure to execute event.preventDefault() to cancel current route change.
Add a controller to your parent state, it will check the $state.current value and if the value match his state will redirect to the child (this is what i have in my application).
EDIT : as requested in comment :
if($state.current.name != 'app.details'){
$state.go('app.details.overview');
}
Related
Is there any problem putting default code inside of a <ui-view> element. It appears to work, but I can't find anything saying one way or another if it's okay to use or not.
My current usage is I want the "default" view to be a list of items. Upon clicking one of those items, it switches to an "editor" child state, which replaces the <ui-view> content with the editor child.
Are there any gotchas I should be aware of before continuing with this approach?
Here is an example of what I'm looking at:
routes.js:
.config(($stateProvider) => {
$stateProvider
.state('admin', {
url: '/admin',
templateUrl: 'admin.html'
})
.state('admin.items', {
url: '/admin/items',
templateUrl: 'admin.items.html'
})
});
admin.html:
<ui-view>Default Stuff Here</ui-view>
items.html:
<p ng-repeat="item in items">{{item}}</p>
Now, I know I can do:
.state('admin.default', {
url: '',
templateUrl: 'admin.default.html'
});
And then put that would show in ui-view. However, that needlessly adds a new state and template file, when it seems to work just fine putting the would-be contents of admin.default.html directly into the ui-view of admin.html.
In my case, I'm not talking about a completely stateless option using otherwise(), I'm talking about a defined parent state with a default child state.
We generally do not put anything inside <ui-view></ui-view>, instead we create a default state and use that.
routerApp.config(function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise('/home');
$stateProvider
.state('home', {
url: '/home',
templateUrl: 'home.html'
})
.state('about', {
// we'll get to this in a bit
});
});
Here the default one is /home, like that you can create a default one.
First, As said in the other answer, we never give any data within the ui-view.. But while dealing with the ui-router, you should give all the specific states in .config()
app.config(function($stateProvider,$urlRouterProvider){
$stateProvider
.state('home',{
url : '/home',
controller: 'homeCtrl',
templateUrl : 'home.html'
}).state('login',{
url : '/login',
controller: 'loginCtrl',
templateUrl : 'login.html'
});
//AND HERE YOU PROVIDE THE DEFAULT PLACE WHERE THE USER IS TO BE REDIRECTED
//IN CASE OF IMPROPER URL
$urlRouterProvider.otherwise('/login');
});
Secondly, If you want to use the <ui-view></ui-view> to hold some data, you certainly can put some data within them.. But make sure that from the state you define, It should not supply any template or templateUrl. So the data within the <ui-view></ui-view> stays as it is.
And third.. Why to keep a default state..
in case you are defining a state admin in your config(), and then in your admin.routes.js file you can define a state as follows..
.state('admin.login',{
url:'',
controller:'loginCtrl',
templateUrl:'admin.login.html'
});
So in case the url is YOURURL/admin It will directly open the login page by default.. so there is no chance of redundancy...
When I make changes to the template from an sub state and I go to the view again the first loaded (so before the template was updated) view is given.
I have tested it with google chrome. internet explorer, microsoft edge all the same problem.
my state
$stateProvider
.state('games', {
abstract: true,
url: "/?username&token",
templateUrl: "games/views/Games.html",
controller: 'GamesController'
})
.state('games.open', {
url: '',
templateUrl: "games/views/GamesOpen.html"
})
.state('games.active', {
url: "/active",
templateUrl: "games/views/GamesActive.html"
})
HTML
<a ui-sref=".open"><button>open games</button></a>
<a ui-sref=".active"><button>active games</button></a>
<div ui-view></div>
The problem seems to be that the views are cached, the transitions work perfectly only I can't view the updated template and always see the first loaded template that was loaded and never the updated version..
A similair question fixes it by adding ?'+ new Date() at the end of the url but this looks like a hack.
you don't have any url in your game.open state: please check that.i guess that is an issue.
When i was creating an app
i used this code :
(well in my case in my certain app )
app.config(function($stateProvider,$urlRouterProvider){
$stateProvider.state('list',{
url:'/list',
templateUrl:'templates/list.html'
});
$stateProvider.state('edit',{
url: '/edit/:noteId',
templateUrl: 'templates/edit.html',
controller:'EditCtrl'
});
$stateProvider.state('add',{
url: '/add',
templateUrl: 'templates/edit.html',
controller:'AddCtrl'
});
$urlRouterProvider.otherwise('/list');
});
and use
a button like this:
New Note
I have app with many main states, one of them is user profile:
$stateProvider.state('profile', {
url: '/profile/',
templateUrl: 'profile/profile.html',
controller: 'Profile',
});
But this is just an container for nested pages with different profile settings. It's template only contains main menu and ui-view for nested states. Controller is only for that menu handling.
One of nested views should be default url and have same URL as parent, so there shouldn't be any suffixes added into url, but I can't achieve that.
Here's what I tried:
$stateProvider.state('profile.details', {
url: '',
templateUrl: 'profile/details.html',
controller: 'ProfileDetails',
});
this is not working at all, at url /profile/ only menu appears and an empty ui-view element. Second approach:
$stateProvider.state('profile.details', {
url: '/',
templateUrl: 'profile/details.html',
controller: 'ProfileDetails',
});
This matches on url /profile// (with 2 slashes at end). At url /profile/ there is still menu and empty ui-view element.
How can I achieve that result? Is this even possible using angular-ui-router?
Make your parent state abstract. This will prevent from going into that state, and force to go to child states only. Abstract states are perfect as templates for child ones. Also get rid of url:
$stateProvider.state('profile', {
abstract: true,
templateUrl: 'profile/profile.html',
controller: 'Profile',
});
Now for your child state define absolute URL
$stateProvider.state('profile.details', {
url: '^profile',
templateUrl: 'profile/details.html',
controller: 'ProfileDetails',
});
That should work.
I am playing with nested view from ui-route, but when I try to access a nested template I get an error. My state:
$stateProvider.state('invoice',{
url: '/invoice',
views: {
'#': {
templateUrl: 'views/invoice.v01/paths/invoice.pocketbits.html'
},
'list#invoice': {
templateUrl: 'views/invoice.v01/paths/database.pocketbits.html'
}
}
});
localhost/#/invoice works fine but when I try to click "ui-sref="list" from inside I get:
Error: Could not resolve 'list' from state 'invoice' at
Object.transitionTo (angular-ui-router.js:3141)
Although my list template is inside the invoice.
Actually the string you give to ui-sref should refer to a state name.
I mean actually you try to go to a state list that you didn't declared.
$stateProvider.state('invoice',{
url: '/invoice',
views: {
'#': {
templateUrl: 'views/invoice.v01/paths/invoice.pocketbits.html'
},
'list#invoice': {
templateUrl: 'views/invoice.v01/paths/database.pocketbits.html'
}
}
});
This state is called "invoice" and have two static nestedview # and list#invoice
You actually can't go to list state.
You may want something like that :
$stateProvider.state('invoice',{
url: '/invoice',
templateUrl: 'views/invoice.v01/paths/invoice.pocketbits.html'
});
$stateProvider.state('invoice.list',{
url: '/list',
templateUrl: 'views/invoice.v01/paths/database.pocketbits.html'
});
To be clear. In you index.html you have a ui-view.
When you do to state invoice you will replace this ui-view with the template from the invoice state. Your invoice state template will have a ui-view inside. When you go to state invoice.list you'll add the invoice.list template into this ui-view.
EDIT :
According to your comment this is the usecase you're looking at.
If you want to have two separate view (ie : when you switch from invoice to list you'll replace the content of the index.html ui-view) you should do this like this :
$stateProvider.state('invoice',{
url: '/invoice',
templateUrl: 'views/invoice.v01/paths/invoice.pocketbits.html'
});
$stateProvider.state('list',{
url: '/list',
templateUrl: 'views/invoice.v01/paths/database.pocketbits.html'
});
Hope it helped. If you provide more information on what you try to achieve i could add an exemple of your case and make it working on plunker.
I started building ionic app on top of the sidemenu starter app. The starter app has a base state 'app' which is abstract and all the sidemenu pages are children of the app for example app.search, app.browse, app.playlists etc.
I have similar hierarchy. However, I want the start page to be some other page, which means it is at the app level.
The states look like this:
$stateProvider
.state('app', {
url: "/app",
abstract: true,
templateUrl: "templates/menu.html",
controller: 'AppCtrl'
})
.state('join', {
url: "/join",
views: {
'menuContent' :{
templateUrl: "templates/join.html",
controller: 'joinCtrl'
}
}
})
.state('app.search', {
url: "/search",
views: {
'menuContent' :{
templateUrl: "templates/search.html",
controller: 'searchCtrl'
}
}
})
.state('app.results', {
url: "/results",
views: {
'menuContent' :{
templateUrl: "templates/results.html",
controller: 'resultsCtrl'
}
}
});
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/join');
When I run the app, the url defaults to
http://192.168.1.4:8100/#/join
and shows a blank page. Obviously, the join.html is not blank. Also, the console.log messages in joinCtrl are not outputted.
I am not able to figure out why is it not loading the join page. When I change the otherwise to point to '/app/search', everything works.
Any idea what's going on? How do I load the initial page by default and then navigate to the 'app.search' state?
I would expect that because the app is abstract - it is there for a reason. To be parent/layout state. In its template should most likely live all other states.
If yes - check this working example I created to demonstrate that. What we need is to mark the join as a child of the app state. Then the 'menuContent' placeholder will be properly searched in the app template:
.state('join', {
parent: 'app',
url: "^/join",
views: {
'menuContent': {
templateUrl: "tpl.join.html",
controller: 'joinCtrl'
}
}
})
There is a working plunker
The definition url: "^/join", is there to support the idea, that the url defined like this:
// if none of the above states are matched, use this as the fallback
$urlRouterProvider.otherwise('/join');
will work even for nested state (join is child of app). See:
Absolute Routes (^)
If you want to have absolute url matching, then you need to prefix your url string with a special symbol '^'.
This is just one way... we can do the similar stuff if the join is not nested state, but then it should target the unnmaed view '' instead of 'menuContent'