I have an application where I wish that two panels (ui-view) work singly.
https://jsbin.com/neroze/edit?js,output
But, when I click in link to change the right panel, the center panel is changed and when I click to change the center panel, the right panel is changed.
I wonder how I can work around this problem.
You should have a main state (a parent state) which defines your views (with controllers and templates, etc...)
Then you can have child states where you can override your views using absolute state definitions.
$stateProvider.state('app', {
url: '/',
abstract: true,
parent: 'app',
views: {
'panelLeft#app': {
templateUrl: 'modules/panel/views/leftPanel.html',
controller: 'LeftPanelCtrl as vm'
},
'panelRight#app': {
templateUrl: 'modules/module1/views/anotherView.html',
controller: 'Module1Ctrl as vm'
}
}
}).state('anotherstate', {
url: '/',
parent: 'app',
views: {
'panelRight#anotherstate': {
templateUrl: 'modules/module2/views/differentView.html',
controller: 'Module2Ctrl as vm'
}
}
});
Note: it's just an example from scractch, may not work for the first run.
Related
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.
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?
I want to inherit states/URLs using parent.child notation in UI router. I don't want to inherit views (or nest views inside other views), or inherit controllers, I just want to inherit URLs. I'm using 'ui.router' as a dependency.
This is an example of a router:
$stateProvider
.state('products', {
url: '/products',
templateUrl: 'view1.html',
controller: 'products#index'
})
.state('products.show', {
url: '/:id',
templateUrl: 'view2.html',
controller: 'products#show'
})
.state('products.buy', {
url: '/:id/:moreParams',
templateUrl: 'view3.html',
controller: 'products#buy'
})
.state('products.sell', {
url: '/:id/:moreParams/:moreParams',
templateUrl: 'view4.html',
controller: 'products#sell'
});
And the controllers are:
angular.module('productsModule', [])
.controller('products#index', function($scope){
})
.controller('products#show', function($scope){
})
.controller('products#buy', function($scope){
})
.controller('products#sell', function($scope){
})
Here, all views are completely different, and I don't want to nest any view inside any other view. Also, all controllers are different too, and I don't want to inherit controllers, they are all separate with different functions.
Here's my expected result. What I want to achieve is Angular to allow me to only inherit URLs, so the URLs become:
/products
/products/:id
/products/:id/:moreParams
/products/:id/:moreParams/:moreParams
(and then have each URL its own view and controller, as specified above)
So far it's not working, and my research is beginning to tell me that this kind of inheritance using parentState.childState is only for when you want to have nested views (which is what I don't want. I only want to re-use URLs).
My workaround is to create router URLs like products_show, that is, using an underscore instead of a dot, so they are treated as new independent URLs rather than inheritance ones. I'm not sure if this is the best idea, mostly because it looks ugly (though it works perfectly).
Perhaps I should just use products_show in case it can't be done with a dot? Ideas?
You can have a parent child relationship between the routes without nesting their respective views. You achieve that by specifying an absolute target for your view. Like this:
$stateProvider
.state('products', {
url: '/products',
templateUrl: 'view1.html',
controller: 'products#index'
})
.state('products.show', {
url: '/:id',
views: {'#': {
templateUrl: 'view2.html',
controller: 'products#show'
}}
})
.state('products.buy', {
url: '/:id/:moreParams',
views: {'#': {
templateUrl: 'view3.html',
controller: 'products#buy'
}}
})
.state('products.sell', {
url: '/:id/:moreParams/:moreParams',
views: {'#': {
templateUrl: 'view4.html',
controller: 'products#sell'
}}
});
By doing that you're basically telling ui-router to render your view inside the unnamed ui-view in your main index.html, thus, overriding the parent view. Instead of looking for a ui-view in the parent view's template.
To understand why this works, you'll have to understand how ui-router decides where to render the view for your route. So, for example, when you do:
.state('parent.child', {
url: '/:id',
templateUrl: 'child-view.html',
controller: 'ChildCtrl'
})
ui-router will by default translate this to something like:
.state('parent.child', {
url: '/:id',
views: {'#parent': {
templateUrl: 'child-view.html',
controller: 'ChildCtrl'
}}
})
which will cause it to look for an unnamed ui-view inside the parent's view template and render the child view's template inside.
You could also specify more than 1 view for a route, like:
.state('parent.child', {
url: '/:id',
views: {
'#parent': {
templateUrl: 'child-view.html',
controller: 'ChildCtrl'
},
'sidebar#parent': {
templateUrl: 'child-view-sidebar.html',
controller: 'ChildViewSidebarCtrl'
}
}
})
In this case, child-view.html will be rendered inside the parent view's unnamed ui-view as before. In addition it will also look for a ui-view="sidebar" in the parent view's template and render child-view-sidebar.html inside.
For more info on this powerful views option and how to specify targets for your view, see the ui-router docs
This is how to write nested states:
$stateProvider
.state('products', {
url: '/products',
templateUrl: 'view1.html',
controller: 'products#index'
})
.state('products.show', {
parent:'products',
url: '/:id',
templateUrl: 'view2.html',
controller: 'products#show'
})
I am writing an AngularJS Application using ui-router. The states 'home' and 'book' are loaded into the (parent) - ui-view element
My setup for the routes is as following :
.config(function ($stateProvider, $urlRouterProvider) {
$urlRouterProvider.otherwise("/home");
$stateProvider
.state('home', {
url: '/home',
templateUrl: '/home2/app'
})
.state('book', {
url: '/book',
templateUrl: '/book/index'
})
.state('book.overview', {
url: '/overview',
templateUrl: '/book/overview'
})
.state('book.edit', {
url: '/edit/:bookid',
templateUrl: '/book/detail',
controller: 'bookeditcontroller'
})
.state('book.create', {
url: '/create',
templateUrl: '/book/detail',
controller: 'bookeditcontroller'
});
});
When the user tiggers the 'book' state (through a href), the template from '/book/index' is loaded and displayed successfully. But on this first request, i also want to load the template from '/book/overview' and displaying it in the child ui-view.
i've already read the topics about the default states under https://github.com/angular-ui/ui-router/wiki/Frequently-Asked-Questions#how-to-set-up-a-defaultindex-child-state
But this is not exactly the behavior i want. Is there a way to tell ui-router when parent state 'book' is loaded, also load 'book.overview' into its (child) ui-view ?
Thanks for you help!
I would say that you will need
Multiple Named Views
This allows us to think in one state - many views
State would look like this
.state('book', {
url: '/book',
views : {
'' : { templateUrl: '/book/index', },
'#book': {templateUrl: '/book/overview' },
}
})
this way, we will place two views into one state.
The first will be injected into index.html/root <div ui-view=""></div>
The second will be placed inside of the templateUrl: '/book/index',
That's how we can play with many views in one (or even more parent, grand parent...) state.
I created a plunker with layout, which does show a bit similar example. The code snippet of the state with many views is:
$stateProvider
.state('index', {
url: '/',
views: {
'#' : {
templateUrl: 'layout.html',
controller: 'IndexCtrl'
},
'top#index' : { templateUrl: 'tpl.top.html',},
'left#index' : { templateUrl: 'tpl.left.html',},
'main#index' : { templateUrl: 'tpl.main.html',},
},
})
In story form:
What I am looking for here is a master-detail setup. The master is in list form and when I click on a link (relative to a particular row/record (or Account in this case)) I want to see the details in the main view (literally, the "main" view: <div class="container" ui-view="main"></div>).
I want to do this and maintain my URL structure (/accounts for the list of Accounts; /accounts/:id for the detailed version) but I want the detail view to use the view that the list was using.
What I currently have
index.html
...
<div class="container" ui-view="main"></div>
...
accounts.js
$stateProvider
.state ('accounts', {
url: '/accounts',
views: {
'main': {
controller: 'AccountsCtrl',
templateUrl: 'accounts/accounts.tpl.html'
}
},
data: { pageTitle: 'Account' }
})
.state ('accounts.detail', {
url: '/:id',
views: {
'main': {
controller: 'AccountDetailCtrl',
templateUrl: 'accounts/detail.tpl.html'
}
},
data: { pageTitle: 'Account Detail' }
});
At this point, the /accounts route works as expected. It displays accounts/accounts.tpl.html correctly in the main view. In that html each line in the repeater links it to its appropriate /accounts/:id URL, which I am handling with the nested state accounts.detail.
What is probably obvious to the majority of you who know more than me about this, my accounts.detail will render to the view main if that named view exists in the template accounts/accounts.tpl.html. That is indeed true.
But that is not what I want. I want the accounts.detail stuff to render in the parent main view; I want the html of accounts/detail.tpl.html to replace the html of accounts/accounts.tpl.html found in index.html: <div class="container" ui-view="main"></div>.
So how could I accomplish this?
MY SOLUTION IN CONTEXT
The trick is, as the answer says, to set up the URL scheme to identify which child state is "default". The way I interpret this code in plain English is that the parent class is abstract with the proper URL and the "default" child class has the "same" URL (indicated by '').
If you need further clarity, just post a comment and I'll share any more guidance.
.config(function config( $stateProvider ) { $stateProvider
.state ('accounts', {
abstract: true,
url: '/accounts',
views: {
'main': {
templateUrl: 'accounts/accounts.tpl.html',
controller: 'AccountsCtrl'
}
},
data: { pageTitle: 'Accounts' }
})
.state ('accounts.list', {
url: '',
views: {
'main': {
templateUrl: 'accounts/list.tpl.html',
controller: 'AccountsListCtrl'
}
},
data: { pageTitle: 'Accounts List' }
})
.state ('accounts.detail', {
url: '/:id',
views: {
'main': {
templateUrl: 'accounts/detail.tpl.html',
controller: 'AccountDetailCtrl'
}
},
data: { pageTitle: 'Account Detail' }
});
Sounds like you simply don't want the views to be hierarchical. To do this, simply change the name of the second state to detail.
Note however, that in doing so you will lose any hierarchical properties of the state tree (the controller code state of accounts for example).
If you want to keep the controllers hierarchical, but perform a replace of the html, I would create another parent above both others that takes care of the controller logic, but only has an extremely simple view <div ui-view=""></div>.
For example:
$stateProvider
.state('app', { url: '', abstract: true, template: 'parent.html', controller: 'ParentCtrl' })
.state('app.accounts', { url: '/accounts', templateUrl: 'accounts.tpl.html', controller: 'AccountsCtrl' })
.state('app.detail', { url: '/accounts/:id', templateUrl: 'detail.tpl.html', controller: 'AccountDetailCtrl' });
You can use '#' to define an absolute path to the ui-view of your choice. For example: "detail#contacts" : { }, where this absolutely targets the 'detail' view in the 'contacts' state. within contacts.html
Source: https://github.com/angular-ui/ui-router/wiki/Multiple-Named-Views