I have the following states declaration (angular v1.5.5):
$stateProvider
.state('appPublic', {
abstract: true,
data: { restricted : false }
})
.state('home', {
url: '/',
parent: 'appPublic',
templateUrl: 'app/views/main.html',
controller: 'mainCtrl'
});
When I open my site, I don't see the main html content (the home state). But, when I remove the parent: 'appPublic' declaration - it works then. So, why I can't to specify the state's parent ?
Every parent, must provide a view target for its child (if using unnamed views are used, i.e parent is not skipped with absolute view naming - Angularjs ui-router not reaching child controller). So this should work
.state('appPublic', {
abstract: true,
data: { restricted : false },
template: '<div ui-view=""></div'
})
now, child view (unnamed view) will be placed in the ui-view target, declared in parent's template
try this:
$routeProvider.when('/userLogin',
{
templateUrl: 'WebrtcLogin.html',
controller: 'UserLoginController'
})
ok, I needed to include template: '<ui-view/>' in my abstract state. The answer is here https://stackoverflow.com/a/33181762/106616
Related
Desired Behavior
I'm using AngularJS and the Angular UI-Router.
I want to allow two child states to share a parent state.
I want the child states to fill a ui-view in the parent state's view with their own view.
I want one of the two child states to have three ui-view's in their view, each filled with views.
Attempt at a diagram:
Parent: <ui-view>
filled by
Child: <ui-view> <ui-view> <ui-view>
filled filled filled
Specific Details for My Situation
I have a state called category-details. Inside the view for this abstract state I have an unnamed ui-view. In one of the two child states (category-details.selected) I want to use multiple named views.
Current WORKING Strategy
Here is the abstract state. Very basic, but included for your reference.
.state('category-details', {
abstract: true,
data: {
pageTitle: 'Category Details'
},
templateUrl: "views/category-details.html",
})
In the category-details.selected state (the state which will have multiple named views), I set the unnamed ui-view of category-details.html to category-details-selected.html:
.state('category-details.selected', {
views: {
'': {
templateUrl: 'views/category-details-selected.html',
controller: 'CategoryDetailsSelectedCtrl'
}
}
})
Inside of the category-details-selected.html view I have three named ui-views:
<div ui-view="firstNamedView"></div>
<div ui-view="secondNamedView"></div>
<div ui-view="thirdNamedView"></div>
Finally, I define a state for setting these three ui-view's to meet the third part of my desired behavior:
.state('category-details.selected.aspect', {
url:"/category-details/:selectedCategory",
views: {
'firstNamedView': {
templateUrl: 'views/first-named-view.html',
controller: 'FirstNamedViewCtrl'
},
'secondNamedView': {
templateUrl: 'views/second-named-view.html',
controller: 'SecondNamedViewCtrl'
},
'thirdNamedView': {
templateUrl: 'views/third-named-view.html',
controller: 'ThirdNamedViewCtrl'
}
}
});
Why My Solution is Awkward and Suboptimal
Adding the category-details.selected.aspect state to set constant elements (the three ui-view's) of the category-details-selected view is unnecessary. It forces creating an extra state every time I want multiple named views.
What I've Tried
I believe I should be able to move the url and views of the category-details.selected.aspect state into the views component of its parent state (category-details.selected). This would look like:
.state('category-details.selected', {
url:"/category-details/:selectedCategory",
views: {
'': {
templateUrl: 'views/category-details-selected.html',
controller: 'CategoryDetailsSelectedCtrl'
},
'firstNamedView': {
templateUrl: 'views/first-named-view.html',
controller: 'FirstNamedViewCtrl'
},
'secondNamedView': {
templateUrl: 'views/second-named-view.html',
controller: 'SecondNamedViewCtrl'
},
'thirdNamedView': {
templateUrl: 'views/third-named-view.html',
controller: 'ThirdNamedViewCtrl'
}
}
})
This resulted in the unnamed ui-view being set correctly, but the three named ui-view's were not filled.
Since selecting the three named ui-view's was the problem, I then attempted to select them with absolute selectors described here instead. This did not fix the problem. I tried:
firstNamedView
firstNamedView#
firstNamedView#category-details.selected
(others of course)
Closing Remarks
Is what I'm imagining possible, is another way better, or is my current method the best? It boils down to assigning the child ui-views of a parent ui-view being set at the same time. I thought the last sentence was too confusing alone, so I included the entire example.
Please let me know if I can provide any more clarification such as versions. Thank you.
Abstract states need their own <ui-view/> for their children to plug into.
Parent state category-details is abstract state. Child state will need some reference ui-view to plug that state into. In your I believe the view /category-details.html does not any ui-view (as you have mentioned that category-details-selected.html contains the ui-view).
Try this:
.state('category-details', {
abstract: true,
data: {
pageTitle: 'Category Details'
},
templateUrl: "views/category-details-selected.html",
})
.state('category-details.selected', {
url:"/category-details/:selectedCategory",
views: {
'firstNamedView': {
templateUrl: 'views/first-named-view.html',
controller: 'FirstNamedViewCtrl'
},
'secondNamedView': {
templateUrl: 'views/second-named-view.html',
controller: 'SecondNamedViewCtrl'
},
'thirdNamedView': {
templateUrl: 'views/third-named-view.html',
controller: 'ThirdNamedViewCtrl'
}
}
})
Here, we are giving abstract view a template, which has ui-view in it, for child to populate.
Have a look at documentation of ui-router: Abstract State for more information.
EDIT: I had assumed that views/category-details.html does not contain any ui-view. However, it was then pointed out that, views/views/category-details.html does have ui-view
This is what works for me:
category-details.html:
<div ui-view=""></div>
category-details-selected.html:
<div ui-view="firstNamedView"></div>
<div ui-view="secondNamedView"></div>
<div ui-view="thirdNamedView"></div>
router:
.state('category-details', {
abstract: true,
data: {
pageTitle: 'Category Details'
},
templateUrl: "../app/atest/category-details.html",
})
.state('category-details.selected', {
url: "/atest",
views: {
'': {
templateUrl: "../app/atest/category-details-selected.html",
// controller: 'approvalsCtrl as vm',
},
'firstNamedView#category-details.selected': {
templateUrl: '../app/atest/first.html',
// controller: 'approvalsCtrl as vm',
},
'secondNamedView#category-details.selected': {
templateUrl: '../app/atest/second.html',
// controller: 'approvalsCtrl as vm',
},
'thirdNamedView#category-details.selected': {
templateUrl: '../app/atest/third.html',
// controller: 'approvalsCtrl as vm',
}
}
})
I could see that you have mentioned you tried out using firstNamedView#category-details.selected, but it didn't worked for you. The above example is working for me. Check if you category-details.html and category-details-selected.html view contain proper ui-view.
Below is how the ui-router state provider has been set for our project. Showing below a part of it.
$stateProvider
.state('Parent', {
url: '/mainpath/:Id',
templateUrl: getViewUrl('Main.html'),
controller: 'MainController',
controllerAs: 'main',
abstract: true,
resolve: {
})
.state('Parent.Child', {
templateUrl: getViewUrl('Child.Html'),
controller: 'ChildController',
controllerAs: 'child',
url: '/child',
requireADLogin: true
})
.state('Parent.Child1', {
templateUrl: getViewUrl('Child1.Html'),
controller: 'Child1Controller',
controllerAs: 'child1',
url: '/child1',
requireADLogin: true
})
.....many more child state.
Parent Path Contains url as '/mainpath/:Id', the Id is part of the parent and based on this value parent runs a number of resolve before loading any of the child state(parent being abstract cannot be navigated to any which way).
The end URL looks like http://something/x.html/mainpath/124353534/child1, the final html contains top section which is parent and below section contains the child
<Html>
<Parent>
<Child>
</Child>
<Parent>
</Html>
On the Parent HTML there is a dropdown list to change the Id in the path '/mainpath/:Id'(this is the problem), this value in the dropdown is captured in the main controller (parent) and the current child state is reloaded with $state.go('passing the new ID cause child value will change based on this Id'). All works fine except that i am not able to change the Id value in the URL.
Can you guys please help with this.
I would store the id's in a service, and inject this service to the different controllers that uses it.
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.
The parent route doesn't contain any view. I put the child routes in a parent to share its name so that the url becomes like this .../sites/site1 or .../sites/site2
$stateProvider
.state('sites', {
url: '/sites',
abstract: true
})
.state('sites.site1', {
url: '/site1',
templateUrl: 'templates/site1.html'
})
.state('sites.site2', {
url: '/site2',
templateUrl: 'templates/site2.html'
})
// ...
// Other routes
But this doesn't seem to work when I go to :
<a ui-sref="sites.site1">Go to first site</a>
<a ui-sref="sites.site2">Go to second site</a>
Nothing's showing up. (Other normal routes are working fine)
There must be a target in parent for a child:
.state('sites', {
url: '/sites',
abstract: true,
// THIS line is essential,
// 1) it will inject the parent template into root
// (index.html) ui-view=""
// 2) and will also create a target for a child view
template: '<div ui-view=""></div>',
})
The reason is: child state is using implicit view naming, expecting that parent will have some unnamed target ui-view=""
I have the following layout:
Sidebar and Headerbar will always be present though their content is context-specific.
I think there are two options here: nested states (sidenav > Headerbar > Content) or with views (if I understand that correctly). I'm still struggling to get my head wrapped around ui-router regardless of how many videos and articles I've read.
Clicking on Sidenav would load a state (or view) into Content and Headerbar would adjust its content based on whatever is loaded into Content.
My sense is that nested states seem like the simplest direct approach, particularly when thinking about inheritance.
Looking at it from another point of view, these seem like they could be siblings (although inheritance issues probably make me wrong). My inkling is that views would allow me more flexibility in the future with sub-items and such.
And of course ng-include and directives could play into this.
Being new to ui-router could someone slap me in the right direction? Where I'm stuck is loading the home view. I want my users to see their dashboard in the Content section once they log in. And then, how do I load new elements into Content as the user navigates from the Sidebar?
One way how to design scenario with 1) side bar, 2) action section and 3) main area could be like in this working example
Firstly the root state. Here is root state named 'index'. It is abstract and could do some resolve for us. It does not effect child state naming and does not extend the url (because is undefined)
$stateProvider
.state('index', {
abstract: true,
//url: '/',
views: {
'#' : {
templateUrl: 'layout.html',
controller: 'IndexCtrl'
},
'top#index' : { templateUrl: 'tpl.top.html',},
'left#index' : { templateUrl: 'tpl.left.html',},
'main#index' : { templateUrl: 'tpl.main.html',},
},
})
The first real state is list, and it inherits from parent but with an attribute parent: 'index', so the parent name is not effecting the state name.
Advantage is, that it could inherit lot of resolved stuff. Also, the root state could be loaded once, for all other parent states
.state('list', {
parent: 'index',
url: '/list',
templateUrl: 'list.html',
controller: 'ListCtrl'
})
This is the real power of UI-Router, because now we can see that child is injecting stuff into two places - 1) action section and 2) main area
.state('list.detail', {
url: '/:id',
views: {
'detail#index' : {
templateUrl: 'detail.html',
controller: 'DetailCtrl'
},
'actions#index' : {
templateUrl: 'actions.html',
controller: 'ActionCtrl'
},
},
})
This way, we can use named views and multi views in real world scenario. Please, never forget how the scope definition goes:
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope properties has nothing to do with the nesting of your states and everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within your site. In this scenario you cannot expect to access the scope variables of parent state views within the views of children states.
Check that all in action here
I just would like to share my experience. There is
similar Q & A: Angular UI Router - Nested States with multiple layouts
and a link to working plunker
The snippet of the state def:
$stateProvider
.state('index', {
url: '/',
views: {
'#' : {
templateUrl: 'layout.html',
controller: 'IndexCtrl'
},
'top#index' : { templateUrl: 'tpl.top.html',},
'left#index' : { templateUrl: 'tpl.left.html',},
'main#index' : { templateUrl: 'tpl.main.html',},
},
})
.state('index.list', {
url: '/list',
templateUrl: 'list.html',
controller: 'ListCtrl'
})
.state('index.list.detail', {
url: '/:id',
views: {
'detail#index' : {
templateUrl: 'detail.html',
controller: 'DetailCtrl'
},
}
In a nutshell, I do use the nesting approach.
It is similar to the "core example" available here http://angular-ui.github.io/ui-router/sample/#/. It is hierarchical (entity list / detail)
And what's more, I use the hidden supper root state:
check the details here Updating resolved objects in ui.router parent states
the examle link
which is handling security related stuff - once, and shared among all child states:
$stateProvider
.state('root', {
abstract: true,
template: '<div ui-view></div>',
resolve: {objectX : function() { return {x : 'x', y : 'y'};}},
controller: 'rootController',
})
.state('home', {
parent: "root",
url: '/home',
templateUrl: 'tpl.example.html',
})
.state('search', {
parent: "root",
url: '/search',
templateUrl: 'tpl.example.html',
})
Hope it does enlighten this a bit, because the power of UI-Router I see in multiviews, view nesting, scope inheritance and the logical state machine behind