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' }
}
});
Related
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'
}
}
})
I'm having some difficulties to understand how UI-Router abstract state work.
Here's the thing :
I have an index.html with only a <ui-view> tag inside its <body>.
There is two main states : Landing and App ( /landing and /app ).
No problem with the landing state for now, because its only a static file ( no child view etc ).
But for my /app state, I need an abstract parent state that cover the whole application ( need to resolve User Profile for each child states ).
The thing is that child of this abstract state also have sub-view. And I can't make those render.
My $stateProvider config (simplified) :
//landing state, no problem here
.state('landing', {
url: '/landing',
templateUrl: 'landing.html'
})
// my abstract parent state, with the resolve
.state('root', {
url: '',
abstract: true,
template: "<div ui-view></div>",
resolve: {
current_user: function (UserFactory) {
return UserFactory.initCurrentUserProfile();
}
}
})
// the child which cannot render the view headerAndSearchbar
.state('root.app', {
url: '/app',
templateUrl: 'app.html',
views: {
'headerAndSearchbar': {
templateUrl: './partials/header/headerAndSearchbar.html'
}
}
})
app.html :
<div ui-view="headerAndSearchbar">
</div>
<div>
This is app.html
</div>
Note that if i remove views declaration in the state root.app, i can see the text "This is app.html".
headerAndSearchbar.html only contains simple html & css
Any ideas ? I'm bashing my head on this- What am I missing?
There are two issues.
Firstly, There are in fact two views, inside of the child 'root.app'.
Unnamed (the one, which is related to templateUrl 'app.html')
named view 'headerAndSearchbar'
And that means, that both most be declared inside of the views : {} setting:
.state('root.app', {
url: '/app',
//templateUrl: 'app.html',
views: {
'' : { templateUrl: 'app.html' },
'headerAndSearchbar': { ... n
}...
But that is not enough.
Secondly, we need absolute naming for the second (named) because it is not related to its parent, but to itself:
.state('root.app', {
url: '/app',
//templateUrl: 'app.html',
views: {
'' : { templateUrl: 'app.html' },
// this is the same as a path to parent view 'headerAndSearchbar#root'
// 'headerAndSearchbar': { ...
// this means ... search in this state, inside of app.html
'headerAndSearchbar#root.app': { ...
}...
Check these for more details:
Angularjs ui-router not reaching child controller
Angular UI Router - Nested States with multiple layouts
I have this in my app.js:
$stateProvider
.state('actionplans', {
url: "/actionplans",
templateUrl: "pages/actionplans.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
})
How can I default load nest view action 'actionplans.summary.html' when called actionplans.html?
There is a working example
The way which will
load some view inside of a parent - and stay on parent
allow child change it when navigating to child
is called Multiple named views:
.state('actionplans', {
url: "/actionplans",
views: {
'': {
templateUrl: "pages/actionplans.html",
//controller : 'ActionplansCtrl'
},
'#actionplans': {
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
}
}
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
//controller : 'ActionplansCtrl'
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
//controller : 'ActionplansCtrl'
})
What we did above, is that we used views : {} object to define two views. First is targeting the index.html (the '') the second is targeting this state view target for children ( the '#actionplans').
views: {
'': { // index.html
...
},
'#actionplans': { // this targets the unnamed view for children
Read more about absolute names here
Another way, is to define some default redirection, but that will disable parent state as a real target (e.g. here Redirect a state to default substate with UI-Router in AngularJS)
Here discuss about AngularJS Routing Using UI-Router, you will get enough idea about nested view and multiple view.
https://scotch.io/tutorials/angular-routing-using-ui-router
I found a simple solution here.
$urlRouterProvider.when('/actionplans', '/actionplans/summary');//<-- Add in this line
$stateProvider
.state('actionplans', {
url: "/actionplans",
abstract: true,/// <-- Add in this line
templateUrl: "pages/actionplans.html",
})
.state('actionplans.planning', {
url: "/planning",
templateUrl: "pages/actionplans.planning.html",
})
.state('actionplans.summary', {
url: "/summary",
templateUrl: "pages/actionplans.summary.html",
})
This will load nest view actionplans.summary.html by default when you call /actionplans. My apology that I did not make this clearer in my question so I post the answer here hopefully it will help someone else with the similar scenario.
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
I have a state that has multiple views declared in it as follows:
$stateProvider
.state('home.details.item', {
url: '^/details',
views: {
'chartsView': {
templateUrl: 'charts.html',
controller: 'chartsCtrl'
},
'gridView': {
templateUrl: 'grid.html',
controller: 'gridCtrl'
},
'detailsView': {
templateUrl: 'details.html',
controller: 'detailsCtrl'
}
}
});
I need to reload one of the views without reloading the whole state, without using $state.go($state.current,null , {reload: true}) , and if possible, from the chartCtrl reload detailsCtrl. Is that possible?
I'd say, that the UI-Router solution should be built arround *states*, not views.
(I created working example here). Other words, if there are
some views which should not be reloaded and
some other views, which should be reloaded
... it calls for state nesting. Let's move that view into child state:
.state('home.details.item', {
url: '^/details',
views: {
'chartsView': {
templateUrl: 'tpl.charts.html',
controller: 'chartsCtrl'
},
'gridView': {
templateUrl: 'tpl.grid.html',
controller: 'gridCtrl'
},
// 'detailsView': {
// templateUrl: 'details.html',
// controller: 'detailsCtrl'
// }
}
})
.state('home.details.item.more', {
views: {
'detailsView#home.details': {
templateUrl: 'tpl.details.html',
controller: 'detailsCtrl'
}
}
})
We also need a state, which will do the reload. We could use other way, e.g. with some changing parameter in state more, but that would mean to change the param value on each call. With this specil state, we can easily reload our state 'more':
.state('reload', {
parent: "home.details.item",
views: {
'detailsView#home.details': {
// this controller will just redirect to 'more' and make it fresh...
controller: ['$state', function($state) { $state.go('^.more')}],
}
}
})
And with these simple controllers we can do all that required stuff:
.controller('chartsCtrl', function ($scope, $state) {
var childName = ".more";
$state.go(childName); // default is a sub state 'more'
})
.controller('detailsCtrl', function ($scope) {
$scope.when = Date.now();
})
Having this: we can call this to reload just details:
<a ui-sref="reload">force reload detail view</a>
Now, when navigating to reload, we will be redirected to state "more" and our view will be rerendered.
SUMMARY:
In general, UI-Router represents state machine. I would strongly suggest:
Do not worry to think in states. Views are just their representation in the DOM.
If there are some features related, they most likely represent state. If others do not relate (should be changed often or rarely) they belong to other state. It could be parent, child or sibling...
Check it here