Passing resolved data to child state in Ui-Router - angularjs

So, I have a state where the resolve spits out an array which shows correctly on the frontend.
But I can't seem to be able to pass the parent resolve data to the child state.
$stateProvider.state('berliner', {
url: '/berlinerliste',
params : {search: 'Berliner 2017'},
resolve: {
fair: function(SearchService, $stateParams) {
return SearchService.getAllExhibitors($stateParams.search);
}
},
views: {
'header': {
templateUrl: 'header.htm'
},
'main':{
templateUrl: 'bl2017.htm',
controller: function($scope, fair){
$scope.fair = fair;
console.log($scope.fair);
}
}
}
})
.state('berliner.exhibitor', {
url: '/{id}',
resolve: {
exhibitor: function($stateParams, fair) {
var slug = $stateParams.id;
return slug;
}
},
views: {
'header': {
templateUrl: 'header.htm'
},
'wop':{
templateUrl: 'exhibitor.htm',
controller: function($scope, exhibitor, $filter){
$scope.fairs = $scope.fair;
console.log($scope.fair);
$scope.chosenexhibitor = $filter("filter")($scope.fairs, {'slug':exhibitor}, true);
console.log($scope.chosenexhibitor);
}
}
}
})
All the console log come out undefined.
What am I missing?
PLUNKR
Here's a Plunkr to examplify the issue.

I would say, that the concept should work.. just:
controller belongs to view. not to all views: {}
E.g. this (wrong)
// each view can have controller,
// but views : {} property 'controller' is not used at all
views: {
'header': {
templateUrl: 'header.htm'
},
'wop':{
templateUrl: 'exhibitor.htm',
},
controller: function($scope, exhibitor, $filter){
$scope.fairs = $scope.fair;
console.log($scope.fair);
$scope.chosenexhibitor = $filter("filter")($scope.fairs, {'slug':exhibitor}, true);
console.log($scope.chosenexhibitor);
}
should be adjusted as that:
views: {
'header': {
templateUrl: 'header.htm'
controller: function($scope, exhibitor, $filter){
$scope.fairs = $scope.fair;
console.log($scope.fair);
$scope.chosenexhibitor = $filter("filter")($scope.fairs, {'slug':exhibitor}, true);
console.log($scope.chosenexhibitor);
}
},
'wop':{
templateUrl: 'exhibitor.htm',
controller: ...
There is an example, how
parent does resolve
child view's controller consumes it
states:
.state('parent', {
url: "/parent",
templateUrl: 'tpl.html',
resolve: {
example: ['$timeout', function($timeout){
return $timeout(function(){
return {x: 1}
},100)
}],
},
})
.state('parent.child', {
url: "/child",
templateUrl: 'tpl.html',
controller: 'ChildCtrl',
})
child controller
.controller('ChildCtrl', ['$scope', 'example', function ($scope, example) {
console.log(JSON.stringify(example));
}])
Check it here

Related

AngularJS uiRouter access controller from other view

I have simple hierarchical state:
angular
.module('App.Module.Suppliers', [])
.config(function($stateProvider) {
return $stateProvider
.state('suppliers', {
url: '/suppliers',
views: {
main: {
templateUrl: 'views/layouts/wrapper.html'
}
}
})
.state('suppliers.edit', {
url: '/:id',
views: {
entity_view: {
templateUrl: 'views/layouts/foo.html'
}
}
})
.state('suppliers.edit.details', {
url: '/edit',
views: {
entity_sub_view: {
controller: function () {
this.foo = {bar: "TEST HEADER"};
},
controllerAs: 'vm',
templateUrl: 'views/layouts/main.html'
}
}
});
});
main and entity_view views are generic for all entities and only entity_sub_view changes with core information.
Now in entity_view I want to use some value like <h2>{{vm.foo.bar}}</h2> but it seems that I can't access vm inside these parent states.
Is there a way to pass this and other parameters to parent states to use?
So I found solution that fits my need. Basically you can set controllerAs to top-most state and access it via $scope:
angular
.module('App.Module.Suppliers', [])
.config(function($stateProvider) {
return $stateProvider
.state('suppliers', {
url: '/suppliers',
views: {
main: {
templateUrl: 'views/layouts/wrapper.html'
controller: function () {
this.foo = null;
}
controllerAs: 'mainCtrl'
}
}
})
.state('suppliers.edit', {
url: '/:id',
views: {
entity_view: {
templateUrl: 'views/layouts/foo.html'
}
}
})
.state('suppliers.edit.details', {
url: '/edit',
views: {
entity_sub_view: {
controller: function () {
this.foo = {bar: "TEST HEADER"};
},
controllerAs: 'vm',
templateUrl: 'views/layouts/main.html'
}
}
});
});
And in some controller do
this.$scope.mainCtrl.foo = {bar: "TEST HEADER"};
In view
{{mainCtrl.foo.bar}}

ui-router loading html but not showing

I have a job state with :jobId parameter.
$stateProvider
.state('employer', {
url: "/",
abstract: true,
templateUrl: '../partials/employer/employerHome.html',
controller: 'UserHomeController',
})
.state('employer.edit', {
url: "^/edit",
templateUrl: '../partials/employer/profile.html',
controller: 'UserHomeController',
})
.state('employer.jobs', {
url: "^/jobs",
templateUrl: '../partials/employer/jobs.html',
controller: 'formController'
})
.state('job', {
parent: 'employer.jobs',
url: '/job/:jobId',
// abstract: true,
controller: 'formController',
onEnter: ['$uibModal', '$state', '$http', '$stateParams', function ($uibModal, $state, $http, $stateParams) {
console.log('Open modal');
var modalInstance = $uibModal.open({
templateUrl: 'myModalContent.html',
backdrop: false,
windowClass: 'right fade',
keyboard: true,
controller: 'PostModalInstanceCtrl',
resolve: {
job: function () {
return $stateParams.jobId;
},
},
}).result.finally(function () {
$state.go('employer.jobs');
console.log('job modal closed')
});
}],
})
.state('system_matches', {
parent: 'job',
url: "/system_matches",
controller: 'TController',
views: {
'job#': {
templateUrl: '../partials/employer/system_matches.html',
}
}
});
when I go to the system_matches state the URL looks good: /employer#/jobs/job/1/system_matches and the HTML is loading in the console but cant see the view.
the job/:jobId state is in a modal and from there I want to go to
the system matches state that is not in a modal.
I guess I need to add a ui-view div. my question is where do I need to put the div and how is it supposed to be named with the id parameter?

Angular app initialization with asynchronous data

I need to initialize my Angular with some User data that the whole app depends on. Therefore I need the initialization to be resolved before the router kicks in and controllers are initialized.
Currently, I wrote the initialization code in a run() block of the angular module. The initialization involves an asynchronous http request to get user data and the rest of the application relies upon the user data.
How can I ensure that the http request is resolved before the router kicks-in initializing the controllers?
I am using the ui-router.
The initialization consists in the following:
1) get cookie 'userId'
2) get User from server (asynchronous http request, the whole app depends upon the User)
3) set authService.currentUser
this is a sample of the code
.run(['$cookies', 'userApiService', 'authService',
function($cookies, userApiService, authService){
var userId = $cookies.get('userId');
userId = parseCookieValue(userId);
userApiService.getOne(userId).then(function(user){
authService.currentUser = user;
});
}])
.config(['$stateProvider', '$urlRouterProvider', '$locationProvider',
function($stateProvider, $urlRouterProvider, $locationProvider) {
$locationProvider.html5Mode(true);
$urlRouterProvider.when('/', '/main');
$stateProvider
.state('login', {
url: '/login',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content': {
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
},
'footer': {
templateUrl: 'views/footer.html',
}
}
})
.state('main', {
url: '/main',
views: {
'content#': {
template: '',
controller: function($state, authService) {
if(!authService.isAuthenticated()) {
$state.go('login');
}
if(authService.isStudent()) {
$state.go('student');
}
if(authService.isAdmin()) {
$state.go('admin');
}
}
}
}
})
.state('student', {
url: '/student',
views: {
'header#': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content#': {
templateUrl: 'views/student.html',
controller: 'StudentCtrl'
},
'footer#': {
templateUrl: 'views/footer.html',
}
}
})
.state('admin', {
url: '/admin',
views: {
'header#': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content#': {
templateUrl: 'views/admin.html',
controller: 'AdminCtrl'
},
'footer#': {
templateUrl: 'views/footer.html',
}
}
})
}])
Expanding on someone's comment, you can create a root state that is a parent to all of your other app's states (children to the root). The root state resolves all the user data and then you can inject the user data to any controller or store it in a service.
$stateProvider
.state('root', {
url: '',
abstract: true,
template: '', // some template with header, content, footer ui-views
resolve: {
// fetch user data
}
})
.state('root.login', {
url: '/login',
views: {
'header': {
templateUrl: 'views/header.html',
controller: 'HeaderCtrl'
},
'content': {
templateUrl: 'views/login.html',
controller: 'LoginCtrl'
},
'footer': {
templateUrl: 'views/footer.html',
}
}
})
.state('root.main', {
url: '/main',
views: {
'content#': {
template: '',
controller: function($state, authService) {
if(!authService.isAuthenticated()) {
$state.go('login');
}
if(authService.isStudent()) {
$state.go('student');
}
if(authService.isAdmin()) {
$state.go('admin');
}
}
}
}
})
... // your other states
The key is that all of your app states must be a child of your root state i.e. root.<name> in your state declaration. This will ensure that no other controller starts until your user data is available. For more information on resolve and how to use it read here. Also, parent and child states.

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.

Parent state is called twice when transition on child state

app.config(function ($stateProvider, $urlRouterProvider, $httpProvider) {
$stateProvider.state('home', {
url: '/',
templateUrl: 'product/product.html',
controller: 'products'
}).state('productdetail', {
url: '/product/:productid',
templateUrl: 'product_details/productdetails.html',
resolve: {
Products: function($stateParams, Product) {
return Product.fetchList($stateParams.productid);
}
},
controller: 'productDetail'
}).state('category', {
url: '/category/:category_code',
templateUrl: 'category/category.html',
resolve: {
Categories: function($stateParams, Category) {
return Category.fetchList($stateParams.category_code);
}
},
controller: 'category'
}).state('category.product', {
// url: '/category/:category_code',
templateUrl: 'category/products.html',
// resolve: {
// Categories: function($stateParams, Category) {
// return Category.fetchList($stateParams.category_code);
// }
// },
// controller: 'category'
})
$urlRouterProvider.otherwise('/');
});
in my controller I use :
$state.transitionTo('category.product')
this will load my controller twice first it call http://localhost:3000/category/cat_code
then
http://localhost:3000/category

Resources