I'm trying to create 2 separate URL states within the same config file.
I was following this template below.
$stateProvider.state('parent', {
data:{
customData1: "Hello",
customData2: "World!"
}
})
.state('parent.child', {
data:{
// customData1 inherited from 'parent'
// but we'll overwrite customData2
customData2: "UI-Router!"
}
});
My code is below. The /reports/moveFrom works fine; however, the /drillDown route is not even registering. Hitting that url sends me back to my apps homepage.
I'm wondering if the parent.child notation is getting messed up by the app.report_moveFrom.drillDown as it has 2x (.)
Any help would be greatly appreciated.
$stateProvider
.state('app.report_moveFrom', {
url: '/reports/moveFrom',
views:{
'main': {
template: require('./moveFrom.html'),
controller: 'moveFromController as $ctrl'
}
},
title: 'moveFrom'
})
.state('app.report_moveFrom.drillDown', {
url: '/drillDown',
views:{
'main': {
template: require('./drillDown.html'),
controller: 'moveFromController as $ctrl'
}
},
title: 'drillDown'
})
Can you please add the code that you using to make a call.
You should be calling something like this:
/reports/moveFrom/drillDown
If you are not doing this, kindly change it once and check
This is the piece from the lib:
$stateProvider
.state('contacts', {
abstract: true,
url: '/contacts',
// Note: abstract still needs a ui-view for its children to populate.
// You can simply add it inline here.
template: '<ui-view/>'
})
.state('contacts.list', {
// url will become '/contacts/list'
url: '/list'
//...more
})
.state('contacts.detail', {
// url will become '/contacts/detail'
url: '/detail',
//...more
})
The point is: each . (dot) in state name represents nesting. I.e
// child of the 'app' state
.state('app.report_moveFrom', {
// child of the above 'app.report_moveFrom'
.state('app.report_moveFrom.drillDown', {
Also, any view targeting, is using by default relative naming. It means, UI-Router searches for (un)named view in the parent state. So, the 'app.report_moveFrom' must contain app.report_moveFrom if we use this syntax as this:
// parent for next state
.state('app.report_moveFrom', {
...
// the default view name resolution is related to parent
.state('app.report_moveFrom.drillDown', {
url: '/drillDown',
views:{
// that ui-view="main" target must be in the above state
'main': {
...
and finally, if we have a child, it inherits the parent url as well
// parent url
.state('app.report_moveFrom', {
url: '/reports/moveFrom',
...
})
//child url - is later extended with parent part
.state('app.report_moveFrom.drillDown', {
url: '/drillDown',
...
// and in runtime this state has '/reports/moveFrom' + '/drillDown'
// i.e.: '/reports/moveFrom/drillDown'
But we can change this default behavior, with just a few tricks.
1) Reset url, to start at root, and 2) target grand parent, with absolute view naming
.state('app.report_moveFrom.drillDown', {
// this sign at the beginning will rest url evaluation - to root level
url: '^/drillDown',
views:{
// we use absolute naming here, so this state will be placed into grand parent 'app'
'main#app': {
...
But the best would be (in my view) simply remove the '.' from the state name, and by default create a sibling (not a child)
.state('app.report_moveFrom', {
// remove dot
//.state('app.report_moveFrom.drillDown', {
.state('app.report_moveFrom_drillDown', {
...and now all will be working by design - because we did not created too much nested state
Related
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
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 am using Angular UI Router , and I have setup two routes
One for all the content pages like /about, /terms etc
$stateProvider.state('sidebarPages.page', {
url: ':slug',
views : {
...
}
});
And now I want to add another for other pages like our-team
$stateProvider.state('sidebarPages.page', {
url: 'our-team',
views : {
...
}
});
The problem is that the second state is ignored when I go to page /our-team and the first one is executed instead which is :slug , and could accept everything.
Is there a way that I can create these two states, one for specific pages , and one that will accept everything and put it in slug param , and based on param I can then bring it from DB.
I created working plunker here. The order decides. Create states with known names, then the one with the slug:
// States
$stateProvider
.state('home', {
url: "/home",
templateUrl: 'tpl.html',
})
.state('other', {
url: "/other",
templateUrl: 'tpl.html',
})
.state('slug', {
url: "/:slug",
templateUrl: 'tpl.html',
})
;
Check it here
I'm using Angular-UI router and in my project I have this structure of pages:
- Main (/main)
-- Table (/main/table/:userid)
-- Info (/main/info)
-- About (/main/about)
In case the user going to the /main I want it to act like the user hit the /main/table/1 without causing url change.
How can I achieve that ?
Here are my states:
$stateProvider
.state('main', {
'url': '/main',
'templateUrl': '/pages/main.html',
'controller': 'MainController',
'resolve': { ... }
})
.state('main.table', {
'url': '/main/table/:userid',
'templateUrl': '/pages/table.html',
})
.state('main.info', {
'url': '/main/info',
'templateUrl': '/pages/info.html',
})
.state('main.about', {
'url': '/main/about',
'templateUrl': '/pages/about.html',
})
I created working plunker here. The trick is to reuse the "main.table" stuff directly in the main state.
We can have main state define like this:
$stateProvider
.state('main', {
'url': '/main',
views: {
'': {
'templateUrl': '/pages/main.html',
'controller': 'MainController',
},
'#main': {
'templateUrl': '/pages/table.html',
'controller': 'TableController',
}
}
})
And these are almost unchanged, just the /main is replaced from url, it will be passed by parent.
.state('main.table', {
'url': '/table/:userid',
'templateUrl': '/pages/table.html',
'controller': 'TableController',
})
.state('main.info', {
'url': '/info',
'templateUrl': '/pages/info.html',
})
.state('main.about', {
'url': '/about',
'templateUrl': '/pages/about.html',
})
And this would be the controller for table view
.controller('TableController', function($scope, $stateParams) {
$scope.userid = $stateParams.userid || 1;
})
Check it all here
The technique used here is: The main state does have two views. One of them is the main - layout template. The second is immediately injecting other view into that layout. via absolute naming '#main' (unnamed view in the state main)
That view (for displaying table) is the same which we use for main.table state. We just check, that if there is no param userid - 1 is used
Read more about this multi views here
View Names - Relative vs. Absolute Names
small extract from example snippet:
$stateProvider
.state('contacts', {
// This will get automatically plugged into the unnamed ui-view
// of the parent state template. Since this is a top level state,
// its parent state template is index.html.
templateUrl: 'contacts.html'
})
.state('contacts.detail', {
views: {
////////////////////////////////////
// Relative Targeting //
// Targets parent state ui-view's //
////////////////////////////////////
// Relatively targets the 'detail' view in this state's parent state, 'contacts'.
// <div ui-view='detail'/> within contacts.html
"detail" : { },
// Relatively targets the unnamed view in this state's parent state, 'contacts'.
// <div ui-view/> within contacts.html
"" : { },
///////////////////////////////////////////////////////
// Absolute Targeting using '#' //
// Targets any view within this state or an ancestor //
///////////////////////////////////////////////////////
// Absolutely targets the 'info' view in this state, 'contacts.detail'.
// <div ui-view='info'/> within contacts.detail.html
"info#contacts.detail" : { }
// Absolutely targets the 'detail' view in the 'contacts' state.
// <div ui-view='detail'/> within contacts.html
"detail#contacts" : { }
// Absolutely targets the unnamed view in parent 'contacts' state.
// <div ui-view/> within contacts.html
"#contacts" : { }
// absolutely targets the 'status' view in root unnamed state.
// <div ui-view='status'/> within index.html
"status#" : { }
// absolutely targets the unnamed view in root unnamed state.
// <div ui-view/> within index.html
"#" : { }
});
stateProvider url property is responsible for browser URL routing mechanism.
stateProvider templateUrl property is a html partial view template which is going to render against a particular state, in our case it is main.
$stateProvider
.state('main', { // **main** is a state.
'url': '/main', // **/main** is a preferred url you want to set in the browser.
'templateUrl': '/main/table/1', // **/main/table/1** is a template to be rendered.
'controller': 'MainController',
'resolve': { ... }
})
States are set up to allow for re-use by injecting parent resolve entries into children, which all works except for the creation of a new package instance. I can't figure out how to determine that the dashboard.package state is the actual state that is be transitioned to. Even if the second parameter meant for the child state is present only one appears in $state.params so I can't check for isUndefined and know that dashboard.package is the state.
Logic for the states are if no second parameter is present no document exists and new instance needs to be created, otherwise state is edit and instance exists.
// Parent dashboard
.state('dashboard', {
url: "/dashboard",
abstract: true,
templateUrl: '/app/dashboard.html',
resolve: {
UserAuth: ...,
GetPackageTypes: ...
}
}
// Parent dashboard package
.state('dashboard.package', {
url: "/package/:packageInstance",
templateUrl: '/app/dashboard/views/package.html',
controller: 'PackageController',
controllerAs: 'packageCtrl',
resolve: {
GetPackageType: [function(){
// HTTP request for package type
}],
CreatePackage: ['$state', '$stateParams', 'GetPackageType',
function($state, $stateParams, GetPackageType){
// ISSUE: Determine if dashboard.package is the actual state???
// if it is then create new package instance, otherwise will drop
// into dashboard.package.edit and package instance will be used
// Example URL for this state: #/dashboard/package/type
// Even if both params exist only see one here so no good
console.log("state.params = ", $state.params);
// Shows previous state name so no good
console.log("state.current = ", $state.current);
console.log("state.current.name = ", $state.current.name);
console.log("state.$current.self.name = ", $state.$current.self.name);
// Returns false so no good
console.log("$state.is = ", $state.is('dashboard.package') );
console.log("$state includes = ", $state.includes('dashboard.package') );
}]
}
}
// Child dashboard package
.state('dashboard.package.edit', {
url: "/edit/:packageInstanceId",
templateUrl: '/app/dashboard/views/package.html',
controller: 'PackageController',
controllerAs: 'packageCtrl',
resolve: {
GetPackageInstance: ['$state', '$stateParams', 'GetPackageType',
function($state, $stateParams, GetPackageType){
// HTTP request for package instance uses package type
// Example URL for this state: #/dashboard/package/type/edit/3858
}],
}
}
ALTERNATIVE SOLUTION
To avoid any more wasted time trying to figure this out, or putting together some funky work around, which will potentially fail in some future update I ended up creating a second abstract state dashboard.package with all my controller, templateUrl, and initial resolve with a single parameter for the package type, and splitting create package out into separate state dashboard.package.create at same level as dashboard.package.edit. Works great with no headache if any finds it useful.
// Parent dashboard
.state('dashboard', {
url: "/dashboard",
abstract: true,
templateUrl: '/app/dashboard.html',
resolve: {
UserAuth: ...,
GetPackageTypes: ...
}
}
.state('dashboard.package', {
url: "/package/:packageType",
abstract: true,
templateUrl: '/app/dashboard/views/package.html',
controller: 'PackageController',
controllerAs: 'packageCtrl',
resolve: {
GetPackage: // injecting parent GetPackageTypes and using parameter
}
}
.state('dashboard.package.create', {
url: "",
resolve: {
CreatePackage: // injecting parent GetPackage
}
}
.state('dashboard.package.edit', {
url: "/edit/:packageinstance",
resolve: {
GetPackageInstance: // injecting parent GetPackage and using parameter
}
}
//$state.current.name will have either name of parent or child.
resolve: {
isChild: function($state, STATES) {
return $state.current.name === STATES.CHILD_NAME;
}
}