Angularjs ui-router abstract state with resolve - angularjs

Is it possible to make an abstract state with ui-router that needs to resolve data? I need to load the top half of a page with profile info, and then have a sub-nav that loads other details at the bottom of the page. The idea is that the abstract state would load the profile info every time regardless of which child state I am on. It doesn't seem to work.
Example:
.state('app.profile', {
abstract: true,
url: 'user/:username',
views: {
content: {
templateUrl: 'views/frontend/profile.html',
controller: 'ProfileCtrl',
resolve: {
data: ['$stateParams', 'ProfileService', function ($stateParams, ProfileService) {
var username = $stateParams.username;
return ProfileService.getProfile(username);
}]
}
}
}
})
.state('app.profile.something', {
url: '',
views: {
profile: {
templateUrl: 'views/frontend/profile.something.html',
controller: 'ProfileCtrl',
resolve: {
data: ['$stateParams', 'ProfileService', function ($stateParams, ProfileService) {
var username = $stateParams.username;
return ProfileService.getProfileSomething(username);
}]
}
}
}
})

Solution here is to distinguish the names of the resolved data. Check this answer:
angular ui-route state parent and resolve (nested resolves)
And its plunker
So in our case we would need this change in parent
resolve: {
dataParent: ['$stateParams', 'ProfileService', function ($stateParams, ProfileService) {
var username = $stateParams.username;
return ProfileService.getProfile(username);
}]
}
and this in child
resolve: {
dataChild: ['$stateParams', 'ProfileService', function ($stateParams, ProfileService) {
var username = $stateParams.username;
return ProfileService.getProfileSomething(username);
}]
}
so, instead of working with resolve : { data: ... } in parent and child we would have these:
// parent state
resolve : { dataParent: ... }
// child state
resolve : { dataChild: ... }
One working example should be better than other words...

Related

AngularJS ui-router wait for parent state to resolve before child state

I have a parent route to finish resolving before I resolve my child states data. Currently, the child state is resolving independently.
In the code below, I want to not resolve the organization in the child state (and this initialize my controller) until the rpe is resolved in the parent state.
Been looking at this for the last couple of hrs, but can't seem to figure it out. Would appreciate any help!
Package info:
ui-router: 1.0.0-beta.1
angular: 1.5.9
Here is the code:
.state('app', {
url: '/app',
abstract: true,
template: '<app layout="row" flex></app>',
resolve: {
// other resolves
rpe: ['authUser', 'RPEService',
function (authUser, RPEService) {
return RPEService.init();
}
]
}
})
.state('app.dashboard', {
url: '/dashboard/?supportchat',
template: '<dashboard flex organization="$resolve.organization"></dashboard>',
data: {
title: 'Dashboard',
hideBack: true
},
resolve: {
organization: ['OrganizationService', 'AuthService', function (OrganizationService, AuthService) {
return OrganizationService.find(AuthService.user.organization_id).then(response => {
return response.data;
});
}]
}
})
Did you tried injecting the parent resolve as a dependency to the child state? Something like this:
.state('app', {
url: '/app',
abstract: true,
template: '<app layout="row" flex></app>',
resolve: {
// other resolves
rpe: ['authUser', 'RPEService',
function (authUser, RPEService) {
return RPEService.init();
}
]
}
})
.state('app.dashboard', {
url: '/dashboard/?supportchat',
template: '<dashboard flex organization="$resolve.organization"></dashboard>',
data: {
title: 'Dashboard',
hideBack: true
},
resolve: {
organization: ['OrganizationService', 'AuthService', 'rpe', function (OrganizationService, AuthService, rpe) {
return OrganizationService.find(AuthService.user.organization_id).then(response => {
return response.data;
});
}]
}
})

How do I share resolve data between Angular ui-route's parent/child states when on different views?

Here's what I have. I need categories to be available at HomeController and NavbarController.
$stateProvider
.state('master', {
abstract: true,
views: {
'navbar#': {
templateUrl: 'views/partials/navbar.default.html',
controller: 'NavbarController as nb',
}
},
resolve: {
categories: function(Category) {
return Category.getList({ depth: 0 });
},
}
})
.state('home', {
parent: 'master',
url: '/',
views: {
'main#': {
templateUrl: 'views/home/home.html',
controller: 'HomeController as hc',
}
},
resolve: {
deals: function(Deal) {
return Deal.getList();
},
}
});
According to the documentation
Child states will inherit resolved dependencies from parent state(s), which they can overwrite. You can then inject resolved dependencies into the controllers and resolve functions of child states.
$stateProvider.state('parent', {
resolve:{
resA: function(){
return {'value': 'A'};
}
},
controller: function($scope, resA){
$scope.resA = resA.value;
}})
.state('parent.child', {
resolve:{
resB: function(resA){
return {'value': resA.value + 'B'};
}
},
controller: function($scope, resA, resB){
$scope.resA2 = resA.value;
$scope.resB = resB.value;
}
It also states:
The resolve keyword MUST be relative to state not views (in case you use multiple views).
The resolve keys MUST be injected into the child states if you want to wait for the promises to be resolved before instantiating the children.

UI Router Child States in Angular not Loading Controllers

I am having an issue loading controllers / templates for each child state. I have a console.log in each state controller, yet they don't fire. Any ideas will be greatly appreciated!
Intent is to always load artist for every child state following.
var stateConfig = ['$stateProvider', function($stateProvider) {
// State Configurations
$stateProvider
.state('artist', {
abstract: true,
url: '/' + artistSlug,
controller: "artistCtrl",
resolve: {
artist: function(artist){
return artist.getArtist();
}
}
})
.state('events', {
parent: 'artist',
url: '',
controller: 'eventsCtrl',
templateUrl: "/templates/artist/events.html"
})
I think you need to revise your states / params
It should go artists -> artists.artist -> artists.artist.events. Then if you go to //site.com/artists/justintimberlake/events it will resolve artist and then events
var stateConfig = ['$stateProvider', function($stateProvider) {
// State Configurations
$stateProvider
.state('artists', {
abstract: true,
url: '/artists'
})
.state('artists.artist', {
url: '/:artistId',
controller: 'artistCtrl',
template: '<div ui-view>Artist Template Wrapper for Artist and Events</div>',
resolve: {
artist: function (artist, $stateParams) {
return artist.getArtist($stateParams.artistId);
}
}
})
.state('artists.artist.events', {
url: '/events',
controller: 'eventsCtrl',
templateUrl: '/templates/artist/events.html',
resolve: {
events: function (artist, $stateParams) {
return artist.getArtistEvents($stateParams.artistId);
}
}
})
}];

ui router - abstract state inside à normal one (3 levels)

i'm trying to get work this combination of sub levels routes.
http://plnkr.co/edit/1nBzppJkB45Ljv5SRW4b?p=preview
i took a sample from ui-route on github and added a parent view (contactg).
$stateProvider
.state('contactg', {
url: '/contactg',
templateUrl: 'contactG.html',
onEnter: function () { console.log("enter contactG"); }
})
.state('contactg.contacts', {
abstract: true,
url: '/contacts',
templateUrl: 'contacts.html',
controller: function ($scope) {
$scope.contacts = [{ id: 0, name: "Alice" }, { id: 1, name: "Bob" }];
},
onEnter: function () {
console.log("enter contacts");
}
})
.state('contactg.contacts.list', {
url: '/list',
// loaded into ui-view of parent's template
templateUrl: 'contacts.list.html',
onEnter: function () {
console.log("enter contacts.list");
}
})
.state('contactg.contacts.detail', {
url: '/:id',
// loaded into ui-view of parent's template
templateUrl: 'contacts.detail.html',
controller: function ($scope, $stateParams) {
$scope.person = $scope.contacts[$stateParams.id];
},
onEnter: function () {
console.log("enter contacts.detail");
}
})
My issues is when i click on an item in contactg.contacts.list, the link generated tends to redirect me to /contacts/:id (detail) instead of /contactg/contacts/:id
is there a way to specify that ?
Thanks
Guillaume.

Angular ui-router to accomplish a conditional view

I am asking a similar question to this question: UI Router conditional ui views?, but my situation is a little more complex and I cannot seem to get the provided answer to work.
Basically, I have a url that can be rendered two very different ways, depending on the type of entity that the url points to.
Here is what I am currently trying
$stateProvider
.state('home', {
url : '/{id}',
resolve: {
entity: function($stateParams, RestService) {
return RestService.getEntity($stateParams.id);
}
},
template: 'Home Template <ui-view></ui-view>',
onEnter: function($state, entity) {
if (entity.Type == 'first') {
$state.transitionTo('home.first');
} else {
$state.transitionTo('home.second');
}
}
})
.state('home.first', {
url: '',
templateUrl: 'first.html',
controller: 'FirstController'
})
.state('home.second', {
url: '',
templateUrl: 'second.html',
controller: 'SecondController'
});
I set up a Resolve to fetch the actual entity from a restful service.
Every thing seems to be working until I actually get to the transitionTo based on the type.
The transition seems to work, except the resolve re-fires and the getEntity fails because the id is null.
I've tried to send the id to the transitionTo calls, but then it still tries to do a second resolve, meaning the entity is fetched from the rest service twice.
What seems to be happening is that in the onEnter handler, the state hasn't actually changed yet, so when the transition happens, it thinks it is transitioning to a whole new state rather than to a child state. This is further evidenced because when I remove the entity. from the state name in the transitionTo, it believes the current state is root, rather than home. This also prevents me from using 'go' instead of transitionTo.
Any ideas?
The templateUrl can be a function as well so you check the type and return a different view and define the controller in the view rather than as part of the state configuration. You cannot inject parameters to templateUrl so you might have to use templateProvider.
$stateProvider.state('home', {
templateProvider: ['$stateParams', 'restService' , function ($stateParams, restService) {
restService.getEntity($stateParams.id).then(function(entity) {
if (entity.Type == 'first') {
return '<div ng-include="first.html"></div>;
} else {
return '<div ng-include="second.html"></div>';
}
});
}]
})
You can also do the following :
$stateProvider
.state('home', {
url : '/{id}',
resolve: {
entity: function($stateParams, RestService) {
return RestService.getEntity($stateParams.id);
}
},
template: 'Home Template <ui-view></ui-view>',
onEnter: function($state, entity) {
if (entity.Type == 'first') {
$timeout(function() {
$state.go('home.first');
}, 0);
} else {
$timeout(function() {
$state.go('home.second');
}, 0);
}
}
})
.state('home.first', {
url: '',
templateUrl: 'first.html',
controller: 'FirstController'
})
.state('home.second', {
url: '',
templateUrl: 'second.html',
controller: 'SecondController'
});
I ended up making the home controller a sibling of first and second, rather than a parent, and then had the controller of home do a $state.go to first or second depending on the results of the resolve.
Use verified code for conditional view in ui-route
$stateProvider.state('dashboard.home', {
url: '/dashboard',
controller: 'MainCtrl',
// templateUrl: $rootScope.active_admin_template,
templateProvider: ['$stateParams', '$templateRequest','$rootScope', function ($stateParams, templateRequest,$rootScope) {
var templateUrl ='';
if ($rootScope.current_user.role == 'MANAGER'){
templateUrl ='views/manager_portal/dashboard.html';
}else{
templateUrl ='views/dashboard/home.html';
}
return templateRequest(templateUrl);
}]
});

Resources