AngularJS $stateProvider - change parent template view from child state - angularjs

Hello everyone this is my first post in Stackoverflow!
I am new to $stateProvider and trying to complete the following behavior:
I am trying to modify and load a new state's parent view from its child state. There is the main view (parent) that has two views, a content view and a navigation menu view on the left hand side. There are multiple child states for a parent state and once the user goes to a child state I would like to load a different navigation menu that was initialized in the parent state.
.state("parent", {
views: {
'contentView': {
templateUrl: baseUrl + "home.html",
controller: "homeController"
},
'navBar': {
templateUrl: baseUrl + "navMenus/navMenu1.html",
controller: "navMenuController"
}
}
})
.state("parent.child", {
views: {
'navBar': {
templateUrl: baseUrl + "navMenus/navMenu2.html",
controller: "navMenuController"
}
}

You can use Absolute Name. In your case, try navBar#parent.

Related

AngularJS. Templates are not included in ui-views, when transitioning between child states of different abstract parents

Templates are not included in ui-views, when transitioning between child states of different abstract parents.
Here is the plunker and link to the editor. Navigate form login (auth.login) state to dashboard (app.dashboard) state, header and footer templates are not included in ui-views.
Example of $stateProvider:
.state('auth', {
abstract: true,
views: {
'app-outer': {
templateUrl: 'auth.html'
}
}
})
.state('auth.login', {
url: '/login',
views: {
'app-inner': {
templateUrl: 'login.html'
}
}
})
.state('app', {
abstract: true,
views: {
'header': {
templateUrl: 'header.html'
},
'app-outer': {
templateUrl: 'app.html'
},
'footer': {
templateUrl: 'footer.html'
}
}
})
.state('app.dashboard', {
url: '/dashboard',
views: {
'app-inner': {
templateUrl: 'dashboard.html'
}
}
});
I've tried passing {reload: true} in the ui-sref-opts and used $state.go() method with {reload: true}.
As a temporary solution I've added header#app, footer#app etc. to the app.dashboard state. But it would be nice to not repeat this everywhere.
'header#app': {
templateUrl: 'header.html'
},
'app-inner': {
templateUrl: 'dashboard.html'
},
'footer#app': {
templateUrl: 'header.html'
}
Your template index.html has app-outer view in it. app.html has header, app-inner, and footer in it. However, you have header and footer as views of app state, which corresponds to index.html. It looks like it attempts to populate those templates when the parent state first becomes active, before the child state (with app.html) is loaded, so it doesn't find the ui-views for the header and footer states.
In other words, it looks like a ui-view element corresponding to a view must exist at the time the state that defines that view becomes active.
See this Plnkr, which insert another level of wrapping state.

UI-Router parent controller doesn't initiate

I'm using AngularJs UI-Router for my app, but I'm with a problem where the parent's controller isn't initiated.
This is my state structure:
.state('main', {
abstract: true,
controller: 'MainController',
controllerAs: 'vm',
resolve: {
config: function($timeout){
return $timeout(function() {
return console.log('loaded')
}, 1000);
}
}
})
.state('home', {
parent: 'main',
url: '/Home',
views: {
'content#': {
templateUrl: 'view/home.html'
}
}
})
.state('contact', {
parent: 'main',
url: '/Contact',
views: {
'content#': {
templateUrl: 'view/contact.html',
}
}
})
The template home.html and contact.html are displaying on the view just fine. But inside the MainController I have just a console.log but it doesn't appear on the console.
If I make some changes, I can make it work. This is the working example:
.state('main', {
abstract: true,
views: {
'main': {
template: '<div ui-view="content"></div>',
controller: 'MainController',
controllerAs: 'vm'
}
}
[...code...]
.state('home', {
parent: 'main',
url: '/Home',
views: {
'content': {
[...code...]
This way, everything works as expected, The view appear and the console from the controller also appear.
But it doesn't seem "right" because I need to create a template just to hold the child states.
Is there a way to make it work with the first option?
Well, to answer:
... Is there a way to make it work with the first option?
Have to say: NO. The point is:
Scope Inheritance by View Hierarchy Only
Keep in mind that scope properties only inherit down the state chain if the views of your states are nested. Inheritance of scope
properties has nothing to do with the nesting of your states and
everything to do with the nesting of your views (templates).
It is entirely possible that you have nested states whose templates populate ui-views at various non-nested locations within
your site. In this scenario you cannot expect to access the scope
variables of parent state views within the views of children states.
So, what happened is - the child state views: {} definition:
.state('contact', {
parent: 'main',
views: {
'content#': {
...
... forced child to skip parent view. Not parent state. It skips a parent view. There is no parent view, from which it could inherit the $scope.
The view of a child state 'contact', is injected directly into root (index.html) ui-view="content", it will not trigger parent view...
So, use the second approach, which is absolutely correct, to achieve what is exepected
Check also these for farther details and working examples:
How to inherit resolve data in ui-router
Nested states or views for layout with leftbar in ui-router?

angular ui router how to wait for child states to be loaded

I'm having problems loading my child states on an abstract true parent state.
This is my parent state
.state('main', {
url: '/main',
templateUrl: 'templates/main.html',
abstract: true
})
This is the child states
.state('main.panels', {
views: {
'ticketsPanel': {
templateUrl: 'templates/ticketsPanel.html'
},
'productsPanel': {
templateUrl: 'templates/productsPanel.html'
},
'categoriesPanel': {
templateUrl: 'templates/categoriesPanel.html'
}
}
})
I have a login page after I login I want to load all 3 child views.
This is the code that process the login.
.controller('loginController', function($scope, Authentication, $log, $state){
$scope.formData = {};
$scope.processForm = function(){
Authentication.login($scope.formData);
var promise = Authentication.getEmployee();
promise.then(function(respond){
localStorage.setItem('employee', JSON.stringify(respond));
$state.go('main.panels');
})
}
})
The $state.go('main.panels') activates the child state of the main state parent, but the problem I'm having is that DOM is showing the element have being loaded but I can only see them partially in my view. It's like they didn't got fully loaded.
My question is how can I wait for all the views in the main.panels to be loaded completely before I transition to that view.
We do have 'resolve' property that can be provided in the definition of each and every state (or view) whatever you want to load. So what angular-ui-router does is that it resolves the 'resolve' property first and only then the HTML template is being rendered on the browser.
You can define the child state in the following way :
.state('main.panels', {
views: {
'ticketsPanel': {
templateUrl: 'templates/ticketsPanel.html',
resolve: function(LoginService){return LoginService};
},
'productsPanel': {
templateUrl: 'templates/productsPanel.html',
resolve: function(LoginService){return LoginService};
},
'categoriesPanel': {
templateUrl: 'templates/categoriesPanel.html',
resolve: function(LoginService){return LoginService};
}
}
})
You can even read the following links for more details :
https://github.com/angular-ui/ui-router/wiki
https://github.com/angular-ui/ui-router/wiki/Nested-States-%26-Nested-Views#inherited-resolved-dependencies
http://www.jvandemo.com/how-to-resolve-angularjs-resources-with-ui-router/
It has been explained in detail. Hope this helps :)
nested states also makes the views nested, meaning the router will look for a named ui-view within the parent template and render it there, and since that's not what you're trying to do you have to implicitly state that it's the parent view (absolute vs relative) like so:
.state('main.panels', {
views: {
'ticketsPanel#': {
templateUrl: 'templates/ticketsPanel.html',
resolve: function(LoginService){return LoginService};
},
'productsPanel#': {
templateUrl: 'templates/productsPanel.html',
resolve: function(LoginService){return LoginService};
},
'categoriesPanel#': {
templateUrl: 'templates/categoriesPanel.html',
resolve: function(LoginService){return LoginService};
}
}
})
'productsPanel#' is like saying 'productsPanel' # nothing which mean the upper parent or root view.
https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views#view-names---relative-vs-absolute-names

Angular ui-router not calling child controller in nested routes

I have my routes set up as below. Its too frustrating that the view in view.tab is loaded but its controller isn't called. I tried without the paramaters, but it still doesn't work as expected. Does anyone have any idea on how to solve this?
$stateProvider
.state('index', {
url: '/',
templateUrl: viewsRoot + 'restaurants/index.htm',
controller: 'RestaurantsCtrl'
})
.state('view', {
url: '/view',
controller: 'RestaurantsViewCtrl',
templateUrl: viewsRoot + '/restaurants/view.htm'
})
.state('view.tab', {
url: '/orders',
// controller: 'OrdersIndexCtrl',
controller: function ($scope) {
alert('This does not run');
},
views: {
"view": {
templateUrl: viewsRoot + '/restaurants/orders.htm'
}
}
});
$urlRouterProvider.otherwise("/");
You need to declare the controller along side the template:
views: {
"view": {
templateUrl: viewsRoot + '/restaurants/orders.htm',
controller: 'MyController' // (or a function, etc.)
}
The UI-Router wiki sort of alludes to this:
If you define a views object, your state's templateUrl, template and templateProvider will be ignored. So in the case that you need a parent layout of these views, you can define an abstract state that contains a template, and a child state under the layout state that contains the 'views' object.
Controllers are paired with a view. So if it ignores the "template" properties defined on the state, it seems to imply that it will ignore the controller too.
If you want all of your named views to share a controller, define an abstract parent state as the wiki suggests.

undefined stateParams using ui-router

Question: For some reason I can't get my controller to recognize my url parameters across sessions.
Background: I have a nested view called modal that takes a parameter, whose url is /modal/:id (eg: /#/modal/1/ or /#/floorplan/1+2/). Ideally, when the user goes to this url, a modal will automatically open with the resource(s) with the given id.
Since the parent state and the child state(modal) are being handled by the same controller, the modal state has a custom data attribute (modalStatus) in its configuration set to true. When this custom attribute is enabled the modal is displayed.
I can currently go from the parent state to the nested state and trigger the modal but when I start a new session or refresh the page with a url like /modal/3, the application fails to read the parameters ($stateParams), which is being logged as an empty object.
I have tried using onEnter and Resolve but I'm not exactly clear on how to use them in this scenario.
Router
$stateProvider
.state('home', {
name: 'home',
url: '/',
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.state('home.modal', {
url: 'modal/:id/',
data: {
modalState: true
},
controller: 'MainCtrl'
})
Relevant part of controller:
$scope.init = function() {
console.log($stateParams);
if ($state.current.data) {
if ($state.current.data.modalState === true) {
$scope.openModal();
}
}
};
$scope.init();
edit: plunkr
You could try adding the params option:
$stateProvider
.state('home', {
name: 'home',
params: {
id: null
},
url: '/',
templateUrl: 'views/main.html',
controller: 'MainCtrl'
})
.state('home.modal', {
url: 'modal/:id/',
data: {
modalState: true
},
controller: 'MainCtrl'
});
The child state home.modal should inherit the params of the parent. On your ui-sref from one state to another, pass the param like so:
<a ui-sref="home.modal({ id: x })"></a>
Then your url would turn out to be /modal/:x (where x is the number).
Also the $stateParams should then show the params as you wished.

Resources