So i have 2 routes set up:
$stateProvider.state('accounts', {
abstract: 'true',
url: "/accounts",
template: '<div ui-view></div>'
}).state('accounts.view', {
url: "/:accountNumber",
views: {
'#': {
templateUrl: 'tpl/account/index.html',
controller: 'AccountController',
controllerAs: 'controller'
}
},
resolve: {
today: ['today', function (today) {
// Return todays date
return today();
}],
tomorrow: ['tomorrow', function (tomorrow) {
// Return tomorrows date
return tomorrow();
}],
lastMonth: ['lastMonth', function (lastMonth) {
// Return lastMonths date
return lastMonth();
}],
account: ['$stateParams', 'AccountService', function ($stateParams, accountService) {
console.log('hi');
return accountService.get($stateParams.accountNumber);
}]
},
data: {
requireLogin: true,
pageTitle: 'Account details'
}
}).state('accounts.create', {
url: '/create',
views: {
'#': {
templateUrl: 'tpl/account/save.html',
controller: 'AccountSaveController',
controllerAs: 'controller'
}
},
resolve: {
account: function () {
return {};
}
},
data: {
requireLogin: true,
pageTitle: 'Create account'
}
}).state('accounts.view.edit', {
url: '/edit',
views: {
'#': {
templateUrl: 'tpl/account/save.html',
controller: 'AccountSaveController',
controllerAs: 'controller'
}
},
data: {
requireLogin: true,
pageTitle: 'Edit account'
}
})
If I click a button decorated with ui-sref='accounts.create' it will navigate to the correct view, but it tries to do the resolve in accounts.view. It seems that the :accountNumber is being confused with /create.
If I refresh my page, it doesn't load the view and still tries to resolve the account.
I have tried a number of ways to solve this.
I would like to have the urls like this:
accounts/:accountNumber - for viewing an account
accounts/create - for creating an account and
accounts/:accountNumber/edit - For editing an account
Is it possible to set up the routes like that?
If your accountNumber is a number indeed, you can define your account.view state as url: "/{accountNumber:int}". That will prevent accounts/create from matching it. Otherwise, you can use some custom regex see docs to achieve the same.
You may also find GitHub issue useful.
Related
I am on angular 1.6. Trying to override value of a ui-router state.
Initial states as follows :
$stateProvider.state('parent', {
abstract: true,
parent: 'ancestor',
url: '/parent',
data: {
authorities: [A,B]
},
views: {
'content#': {
templateUrl: 'app/scripts/myapp/module/parent.html'
}
}
})
.state('child', {
parent: 'parent',
abstract: true,
views: {
'tabs': {
templateUrl: 'app/scripts/myapp/module/submodule/child.html',
controller: 'ChildController',
controllerAs: 'childController'
}
}
})
.state('grandChild', {
parent: 'child',
url: '/{dataSource:(?:option1|option2|option3)}',
views: {
'body': {
templateUrl: 'app/scripts/myapp/module/submodule/grandchild.html',
controller: 'GrandChildController',
controllerAs: 'grandChildController'
},
},
resolve: {
groups: ['MyService', '$transition$', function (MyService, $transition$) {
var selectedSource = $transition$.params().dataSource ? $transition$.params().dataSource : 'option1';
return MyService.getGroups(selectedSource);
}]
}
})
Then in one of sub-implementation I am trying to override value of url - adding option4 for one of the state.
var states = $stateRegistry.deregister('grandChild');
$stateRegistry.register(states[0]);//There is one more child. For simplicity not showing here.
states[1].url.pattern = '/{dataSource:(?:option1|option2|option3|option4)}';
$stateRegistry.register(states[1]);
when I do following on 'grandChild'
$state.go('grandChild', {dataSource: 'option4'});
I get error as
Param values not valid for state 'grandChild'. Invalid params: [ dataSource ]
when I inspect $state
I see something weird.
$state.$current.params.datasource.type.pattern = /(?:option1|option2|option3)/
$state.$current.url.pattern = "/{dataSource:(?:option1|option2|option3|option4)}"
$state.$current.url._compiled[0] = /((?:option1|option2|option3))/
I am working on an AngularJS app. I've gotten quite a bit done but am stuck trying to implement a resolve using ui-router and uiBreadcrumbs. So here is the problem, how do I implement a resolve which can work well with magnification, the same way controllers are implemented? I have included sample code below for brevity from uiBreadcrumbs. Look at the last state home.userList.detail.
angular.module('yourModule').config(function($stateProvider) {
$stateProvider
.state('home', {
url: '/',
views: {
'content#': {
templateUrl: ...
}
},
data: {
displayName: 'Home'
}
})
.state('home.usersList', {
url: 'users/',
views: {
'content#': {
templateUrl: ...
}
},
data: {
displayName: 'Users'
}
})
.state('home.userList.detail', {
url: ':id',
views: {
'content#': {
templateUrl: ...
}
},
data: {
displayName: '{{ user.firstName }} {{ user.lastName | uppercase }}'
}
resolve: {
user : function($stateParams, userService) {
return userService.getUser($stateParams.id);
}
}
});
Wrap the function in an array object just like a controller:
resolve: {
user : [
'$stateParams', 'userService',
function($stateParams, userService) {
return userService.getUser($stateParams.id);
}
]
}
index.html
--navbar.html
--content.html
--customer.html
--netScore.html
--useExp.html
--useExpNest1.html
--useExpNest2.html
--internalPerformance.html
--leftNavPanel.html
I have this kind of view structure and I want to load them all at once so I'm planning to put this in a single state. I saw this answer but it seems that its only applicable for a simple/double nested views(I have 3 or more nested views). How can I put this in a single state, or is there a better way if not possible?
EDIT
I've come up with this solution and it works somehow.
.state('index', {
url: '/',
views: {
'': {
templateUrl: 'app/modules/bulletin/views/index.view.html',
controller: 'indexController'
},
'navbar#index': {
templateUrl: 'app/modules/bulletin/views/index/navbar.view.html',
controller: 'navbarController'
},
'content#index': {
templateUrl: 'app/modules/bulletin/views/index/content.view.html',
controller: 'contentController'
},
'leftNavPanel#index': {
templateUrl: 'app/modules/bulletin/views/index/leftNavPanel.view.html',
controller: 'contentController'
}
}
})
.state('index.content', {
views: {
'customer#index': {
templateUrl: 'app/modules/bulletin/views/index/content/customer.view.html'
},
'internalPerformance#index': {
templateUrl: 'app/modules/bulletin/views/index/content/internalPerformance.view.html'
}
}
})
.state('index.content.customer', {
views: {
'netScore#index.content': {
templateUrl: 'app/modules/bulletin/views/index/content/customer/netScore.view.html'
},
'useExp#index.content': {
templateUrl: 'app/modules/bulletin/views/index/content/customer/useExp.view.html'
}
}
})
.state('index.content.customer.useExp', {
views: {
'useExpNest1#index.content.customer': {
templateUrl: 'app/modules/bulletin/views/index/content/customer/useExp/useExpNest1.view.html'
},
'useExpNest2#index.content.customer': {
templateUrl: 'app/modules/bulletin/views/index/content/customer/useExp/useExpNest2.view.html'
}
}
})
And then add this code to the indexController(most parent controller)
$state.go('index.content');
$state.go('index.content.customer');
$state.go('index.content.customer.useExp');
But this answer is still wrong because, let's say that netScore.html has some child views, we will create route for it then go to that state, but netScore and useExp states are on the same level so only one of them will be loaded if we use
$state.go('index.content');
$state.go('index.content.customer');
$state.go('index.content.customer.netScore');
$state.go('index.content.customer.useExp');
EDIT 2
Here's a plunker of what I've done so far. The view names are slightly different but you will see clearly the problem there
You can use a combination of named views plus abstract: true property to load child views by default
angular.module('sampleModule', [
'ui.router'
]);
angular.module('sampleModule')
.config(['$stateProvider', '$urlRouterProvider', function($stateProvider, $urlRouterProvider) {
$urlRouterProvider.when('','/');
$stateProvider
.state('main', {
url: '',
abstract: true,
templateUrl: 'main.view.html'
})
.state('main.load', {
url: '/',
abstract: true,
views:{
'content':{
templateUrl:'content.view.html',
},
'navbar':{
templateUrl:'navbar.view.html',
}
}
})
.state('main.load.customer', {
url: '',
abstract: true,
views:{
'customerPerception':{
templateUrl:'content-customerPerception.view.html'
},
'customerExperience':{
templateUrl:'content-customerExperience.view.html'
}
}
})
.state('main.load.customer.netTrustScore', {
url: '',
abstract: true,
views: {
'netTrustScore': {
templateUrl: 'content-customerPerception-netTrustScore.view.html'
},
'useExperience': {
templateUrl: 'content-customerPerception-useExperience.view.html'
},
'trustStatements': {
templateUrl: 'content-customerPerception-trustStatements.view.html'
}
}
})
.state('main.load.customer.netTrustScore.somethingElse', {
url: '',
views: {
'abc': {
templateUrl: 'content-customerExperience-customerComplaints.view.html'
},
'': {
templateUrl: 'content-customerExperience-networkQualityIndex.view.html'
}
}
})
;
}])
.controller('mainController', ['$scope', '$state', function($scope, $state) {
console.log('mainController initialized!');
}]);
here's a plnkr
https://plnkr.co/edit/BBAeWjnGbTsbO1lMguU9?p=preview
Thanks to the guys from AngularJS group in FB. The problem is I put two sibling views in two different states. UI router cant load two states at the same time. So the solution is to put all same level views in a single subState.
Lets assume we have this kind of structure:
index.html
--navbar.html
--content.html
--customer.html
--netScore.html
--netScoreNest1.html
--netScoreNest2.html
--useExp.html
--useExpNest1.html
--useExpNest2.html
--internalPerformance.html
--leftNavPanel.html
the proper routing for this would be like this
.state('index', {
url: '/',
views: {
'': {
templateUrl: 'index.view.html',
controller: 'mainController'
},
'navbar#main': {
templateUrl: 'index/navbar.view.html'
},
'content#main': {
templateUrl: 'index/content.view.html'
},
'leftNavPanel#main': {
templateUrl: 'index/leftNavPanel.view.html'
}
}
})
.state('index.subLevel', {
views: {
'customer#index': {
templateUrl: 'index/content/customer.view.html'
},
'internalPerformance#index': {
templateUrl: 'index/content/internalPerformance.view.html'
}
// IF LEFTNAVPANEL OR NAVBAR HAVE SUB VIEWS, PUT IT HERE
}
})
.state('index.subLevel.subLevel2', {
views: {
'netScore#index.subLevel': {
templateUrl: 'index/content/customer/netScore.view.html'
},
'useExp#index.subLevel': {
templateUrl: 'index/content/customer/useExp.view.html'
}
// IF INTERNALPERFORMANCE HAVE SUB VIEWS, PUT IT HERE
}
})
.state('index.subLevel.subLevel2.subLevel3', {
views: {
'netScoreNest1#index.subLevel.subLevel2': {
templateUrl: 'index/content/customer/netScore/netScoreNest1.view.html'
},
'netScoreNest2#index.subLevel.subLevel2': {
templateUrl: 'index/content/customer/netScore/netScoreNest2.view.html'
},
'useExpNest1#index.subLevel.subLevel2': {
templateUrl: 'index/content/customer/useExp/useExpNest1.view.html'
},
'useExpNest2#index.subLevel.subLevel2': {
templateUrl: 'index/content/customer/useExp/useExpNest2.view.html'
}
}
})
And then in mainController, load the inner most child state, this will automatically load the views of all its parent(up to topmost parent state 'index')
$state.go('index.subLevel.subLevel2.subLevel3');
And thats it. And also here's a plunker to make it easier to understand. (Views and structure are slightly different from this post different. Too lazy to edit)
For some reason, my resolvedData is not seeing by controllers when using multiple named views (angular-ui ui-router). Has anyone faced this issue?
$stateProvider
.state('page',{
abstract: true,
templateUrl: ...,
controller: abstractController
})
.state('page.index',
url: '/page',
resolve : {
resolvedData: function(CONSTANTS){ return CONSTANTS.data;}
},
views: {
home: {templateUrl: ...,
controller: function(resolvedData){
....
}
},
list: {templateUrl: ...,
controller: function(resolvedData){
....
}
},
edit: {templateUrl: ...,
controller: function(resolvedData){
....
}
}
}
)
The error it gives me is: Error: [$injector:unpr] Unknown provider: resolvedDataProvider <- resolvedData. It is somehow interesting because it happens only in one of the views but not in the others.
I created small working example, showing that your stuff should work
This would be the CONSTANTS:
.factory('CONSTANTS', function() {
return { data: { name : "some name", number : "some number"} };
})
And the same (just explicitly annotated DI) state def:
// States
$stateProvider
.state('page', {
abstract: true,
template: '<div>'
+ '<div ui-view="home"></div>'
+ '<div ui-view="list"></div></div>',
controller: 'abstractController'
})
.state('page.index', {
url: '/page',
resolve: {
resolvedData: ['CONSTANTS',
function(CONSTANTS) {
return CONSTANTS.data;
}
]
},
views: {
home: {
templateUrl: 'tpl.html',
controller: ['resolvedData','$scope',
function(resolvedData, $scope) {
console.log(resolvedData);
$scope.resolvedData = resolvedData;
}
],
},
list: {
template: '<div>list view</div>'
}
}
})
So, the draft of the resolve used above is working. It is the right way...The resolve function is provided with some service ... and returns its property data.
Check that all here
How can I get ui-router ui-sref to point to a URL
Here's my link:
<a ui-sref="admin.overview"</a>
and my configuration
var admin = {
name: 'admin',
url: '/admin',
views: {
'root': {
templateUrl: '/Content/app/admin/partials/home.html',
},
'content': {
templateUrl: '/Content/app/admin/partials/overview.html',
},
}
};
var adminContent = {
name: 'admin.content',
parent: 'admin',
url: '/:content',
views: {
'root': {
templateUrl: '/Content/app/admin/partials/home.html',
},
'content': {
templateUrl: function (stateParams) {
return '/Content/app/admin/partials/' + stateParams.content + '.html';
},
}
}
};
$stateProvider.state(admin).state(adminContent)
This works if I point to a like with ui-sref='admin' but how can I get it to point to /admin/overview? or the link works if I code href="/admin/overview" myself? However the ui-sref doesn't create anything with the ui-sref="admin.overview".
Here is the documentation for ui-sref: https://github.com/angular-ui/ui-router/wiki/Quick-Reference#ui-sref
Essentially, you need to enter the desired state, and any parameters that you want to include. For your example, I think:
<a ui-sref="admin.content({ content: 'overview' })">Overview Page</a>