I have some states defined like this:
$stateProvider.state('main.product', {
url: '',
abstact: true,
views: {
'': {
templateUrl: 'product/index.html'
},
'sidebar': {
templateUrl: 'product/sidebar.html'
}
}
});
$stateProvider.state('main.product.overview', {
url: '/products/:product_id',
templateUrl: 'product/overview.html',
controller: 'ProductOverviewController'
});
In my 'main wrapping' controller, i'd like to access the abstract states views, like this:
angular.module('myApp').controller('MainController', function($rootScope, $scope, $state, $stateParams) {
console.log($state.$current.views.sidebar.templateUrl);
});
This works if i'm in the parent state main.product. However, as expected, this returns undefined for the child state, main.product.overview.
How can I access views from a child state?
I know I could use a data object, eg:
$stateProvider.state('main.product', {
url: '',
abstact: true,
views: {
'': {
templateUrl: 'product/index.html'
},
'sidebar': {
templateUrl: 'product/sidebar.html'
}
},
data: {
'sidebar': {
templateUrl: 'product/sidebar.html'
}
}
});
Then access like this:
console.log($state.current.data.sidebar.templateUrl)
But this is not clean, the templateUrl would be duplicated.
Is it possible to access views from a child state?
You can refer to any state by using get method
$state.get('main.product').views.sidebar.templateUrl
Related
I'm building an App with nested states using UI-Router.
So far the first state resolves OK and returns an array of objects (fair) which displays on the frontend.
$stateProvider.state('berliner', {
url: '/berlinerliste/',
views: {
'header': {
templateUrl: 'header.htm'
},
'main':{
templateUrl: 'bl2017.htm'
}
},
params : {search: 'Berliner 2017'},
resolve: {
fair: function(SearchService, $stateParams) {
return SearchService.getAllExhibitors($stateParams.search);
}
}
})
$stateProvider.state('berliner.exhibitor', {
url: '/berlinerliste/{id}',
views: {
'header': {
templateUrl: 'header.htm'
},
'main':{
component: 'exhibitor'
}
},
resolve: {
exhibitor: function($stateParams, fair) {
return $filter("filter")(fair, {'slug':$stateParams.id}, true);
}
}
})
When I click on one of the objects though, nothing happens.
The nested state resolve should take the fair array and filter out the object which 'slug':$stateParams.id.
This is the part on the templateUrl: 'bl2017.htm' where I assign the id parameter to the nested state:
<article ng-repeat="exhibitor in datafairs" ui-sref="exhibitor({ id: exhibitor.slug })" >
Oh, and the nested state has a component binded to it:
.component('exhibitor', {
bindings: { exhibitor: '<' },
templateUrl: 'exhibitor.htm',
})
I have configured the following ui-router.
app.config(['$stateProvider', function ($stateProvider) {
$stateProvider
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return codeService.getPost($stateParams.id)
}]
}
}
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
controller: 'EditorCtrl'
})
.state('global.editor.panels', {
controller: 'PanelsCtrl',
params: { layout: 'horizontal' },
templateUrl: function (params) { return "/htmls/" + params.layout + '.html' }
}
}])
app.controller('EditorCtrl', ['$scope', '$state', function ($scope, $state) {
$scope.layout = "horizontal";
$scope.$watch('layout', function () {
$state.go('global.editor.panels', { layout: $scope.layout });
});
}]);
As a result, https://localhost:3000/#/new in a browser leads to (the state global.editor, then to) the state global.editor.panels.
Now, I want to add a parameter connected:
I don't want it to be shown in the url
https://localhost:3000/#/new in a browser makes connected to be false, and https://localhost:3000/#/newTRUE in a browser makes connected to be true
connected can be past into the controller EditorCtrl and PanelsCtrl
connected can be available in the resolve of global.editor; ideally, we could resolve different objects according to the value of connected.
Does anyone know how to accomplish this?
You can add resolve for new and newTRUE:
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
resolve: {
isConnected: function() {
return false;
}
},
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
resolve: {
isConnected: function() {
return true;
}
},
controller: 'EditorCtrl'
})
And in EditorCtrl (or PanelsCtrl) you can use it like:
app.controller('EditorCtrl', ['$scope', '$state', 'isConnected', function($scope, $state, isConnected) {
console.log("connected : " + isConnected); // you can save this value in Service and use it later.
...
}]);
You can use classic approach - in resolve
Or you can use hidden parameters from angular ui router.
Define params : {isConnected' : null} in your parent global state.
In:
global.newTRUE - set value in $state config
global.new - set value in $state config
global.editor.panels - set parameters in transition/go or ui-sref
definition is like this:
$stateProvider
.state('global.newTRUE', {
url : '/:newTRUE',
params : {
'isConnected' : false
}
});
}
and in controller you get in from $stateParams.
Problem with this approach is hidden parameters are loses in refresh page, except if is set default value
You can surely use the params of UI-Router states' config to not show it in URL and achieve all mentioned points.
Also, as per #2, you need connected to be false for /new and true for /newTRUE. We can do so by passing true or false as default value for those states. Something like this:
$stateProvider
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
params: { connected: null },
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return codeService.getPost($stateParams.id)
}]
}
}
.state('global.new', {
url: '/new',
templateUrl: '/htmls/editor.html',
params: { connected: false }, // default false for /new
controller: 'EditorCtrl'
})
.state('global.newTRUE', {
url: '/newTRUE',
templateUrl: '/htmls/editor.html',
params: { connected: true }, // default true for /newTRUE
controller: 'EditorCtrl'
})
.state('global.editor.panels', {
controller: 'PanelsCtrl',
params: { layout: 'horizontal', connected: null },
templateUrl: function (params) { return "/htmls/" + params.layout + '.html' }
}
For #3, In order to access connected in your controllers (EditorCtrl and PanelsCtrl) you can inject $stateParams to controller and use $stateParams.connected to get it.
For #4, (This is more or less similar to achieveing #3)
Just like you get $stateParams.id, you can have $stateParams.connected as well, which you can use to resolve different objects according to the value of connected. Something like this:
.state('global.editor', {
url: '/posts/editor/{id}',
templateUrl: '/htmls/editor.html',
params: { connected: null },
controller: 'EditorCtrl',
resolve: {
post: ['$stateParams', 'codeService', function ($stateParams, codeService) {
return $stateParams.connected ?
codeService.getPost($stateParams.id) :
codeService.getSomethingElse($stateParams.id)
}]
}
}
But, for that to work, make sure that you are passing connected as params when you visit global.editor state (using $state.go or ui-sref)
Hope this helps!
How do I setup ui-router to allow views inheritance? Currently, I have my ui-router setup similar to as follows,
$stateProvider
.state('public', {
url: '/public',
templateUrl: 'partials/public.html',
controller: 'PublicController'
})
// abstract parent
.state('private', {
abstract: true,
views: {
'': {
template: '<ui-view></ui-view>'
},
'nav': {
templateUrl: 'partials/nav.html'
}
}
})
// concrete private child state
.state('private.pageone', {
url: '/pageone',
views: {
'': {
templateUrl: 'partials/pageone.html',
controller: 'PageOneCtrl'
}
}
})
Everything is running fine, except that the parent nav view is not being inherited, and so I can't see the nav in the child state display. On the other hand, if I get rid of the parent state and then move the nav view directly to the child state... I can display the nav. Can anybody explain to me why I can't seem to be able to inherit the nav, even though the child is specified as private.pageone (same as parent: 'private').
The docs for ui-router mention that relative views actually target the template in the parent state. So to be able to use relative views in this particular situation we would have to do the following.
$stateProvider
.state('public', {
url: '/public',
templateUrl: 'partials/public.html',
controller: 'PublicController'
})
// abstract parent
.state('private', {
abstract: true,
views: {
'': {
template: '<ui-view></ui-view>'
}
}
})
// concrete private child state
.state('private.pageone', {
url: '/pageone',
views: {
'': {
templateUrl: 'partials/pageone.html',
controller: 'PageOneCtrl'
},
// target the <div ui-view="nav"></div> in the parent template
'nav': {
templateUrl: 'partials/nav.html'
}
}
})
Alternatively, it would be possible to use absolute views instead of relative ones, as in the following.
$stateProvider
.state('public', {
url: '/public',
templateUrl: 'partials/public.html',
controller: 'PublicController'
})
// abstract parent
.state('private', {
abstract: true,
views: {
'': {
template: '<ui-view></ui-view>'
},
// note how nav has been specified absolutely
'nav#private': {
templateUrl: 'partials/nav.html'
}
}
})
// concrete private child state
.state('private.pageone', {
url: '/pageone',
views: {
'': {
templateUrl: 'partials/pageone.html',
controller: 'PageOneCtrl'
}
}
})
Is it possible to define multiple views in child state with parent child state relationship using UI-Router?
I have the following code in my config
$urlRouterProvider.otherwise("/child");
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
}
})
.state('parent.child', {
url: '/child',
views: {
'state1#parent.child': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
});
I verify that my parent.html is showing up, but my child.html is not
If I move my child.html to the parent views object like
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
'state1#parent.child': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
})
Than child.html works.
I verify using console.log($state.$current.name); in my parentCtrl that my current state is parent.child.
Can someone give me some hint?
Thanks
There is a working plunker
I adjusted your states and mostly child views : {} like this
$stateProvider
.state('parent', {
abstract: true,
views: {
'parent': {
templateUrl: "parent.html",
controller: "parentCtrl as parentCtrl"
},
}
})
.state('parent.child', {
url: '/child',
views: {
//'state1#parent.child': {
'view1#parent': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
// the same as view2#parent
'view2': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
'view3#': {
templateUrl: "child.html",
controller: "childCtrl as childCtrl"
},
}
the construct '...#parent.child' is wrong, because it is a absolute naming... trying to say, that we search a ui-view="..." inside of the 'parent.child' state. And that is not the case.
So, let's place this into index:
// place for parent
<div ui-view="parent"></div>
// place for child view number 3
place in index for view 3
<div ui-view="view3"></div>
And parent template could be like this
<div ui-view="view1"></div>
<div ui-view="view2"></div>
Then the above state def will target view1 with absolute naming, the same for view3 - being in index.html; view2 will go to parent with relative name
check it here
I have few common views (like breadcrumbs, notifications, sidebar etc) defined in an abstract state and few other child states inheriting them. I want to modify a view in the parent (ie: adding a new menu item to sidebar view) when inside a child state. I think I can use view-name#parent-state to refer to that view, but not sure how to get it to work. If it's not possible, am I trying to misuse the inheritance behavior in UI-Router? What's the correct way to implement such functionality in Angular/UI-Router? Can I do this even without using UI-Router?
Here's what I was trying to do:
$stateProvider
.state('posts', {
url: '/posts',
abstract: true,
views: {
"main": { template:'<div ui-view="main-content"></div>'},
"breadcrumbs": { templateUrl: 'breadcrumb.html' },
"navigation": { templateUrl: 'navigation.html' },
"notifications": { templateUrl: 'notifs.html' }
}
})
.state('posts.index', {
url: '',
views: {
"navigation#posts": { templateUrl: 'breadcrumb_mod.html' },
"main-content": { templateUrl: 'partial-config.html' }
}
});