Reputation: 154454
My application has four logical states:
pages.list (lists pages)
pages.create (create a new page)
pages.item (show a single page)
pages.item.edit (edit a single page)
The states pages.list
and pages.item
share one base template (a traditional three column layout), but the states pages.create
and pages.item.edit
have a totally different template (a full-page editor).
What's the best way to express this with ui-router?
Upvotes: 3
Views: 2026
Reputation: 1446
I would say that using abstract states
is the way to go. In documentation, they clearly outline the benefits and some of them fit your use case pretty nicely (emphasis mine):
Some examples of how you might use an abstract state are:
- To prepend a url to all child state urls.
- To insert a template with its own ui-view(s) that its child states will populate.
- Optionally assign a controller to the template. The controller must pair to a template.
- Additionally, inherit $scope objects down to children, just understand that this happens via the view hierarchy, not the state hierarchy.
- To provide resolved dependencies via resolve for use by child states.
- To provide inherited custom data via data for use by child states or an event listener.
- To run an onEnter or onExit function that may modify the application in someway.
- Any combination of the above.
With that understanding, your app can be structured as follows:
Create two templates that your pages can use. Let's call them three_columns.html
and one_column.html
. For 3-column layout, make sure you name your ui-view
accordingly:
<!-- three_columns snippet -->
...
<body>
<div ui-view="left"></div>
<div ui-view="middle"></div>
<div ui-view="right"></div>
</body>
Then, one-column template:
<!-- one_column snippet -->
...
<body>
<div ui-view></div>
</body>
Declare two abstract states
. Let's called them oneCol
and threeCols
. Note that since both of them declare url: '/pages'
, all inheriting child states will have /pages
automatically prepended to their own URLs.
$stateProvider
.state('oneCol', {
abstract: true,
url: '/pages',
templateUrl: 'layouts/one_column.html'
}).state('threeCols', {
abstract: true,
url: '/pages',
templateUrl: 'layouts/three_columns.html'
});
Declare your states which extend abstract states
above. Suppose we call them list
, create
, view
, and edit
. Here, threeCols.list
and threeCols.view
will have to declare 3 templateUrl
s, one for each named ui-view
. On the other hand, oneCol.create
and oneCol.edit
will declare their unnamed templateUrl
normally. The convention I usually use is to create a folder specific to each state, since it's good for organizing sub-view templates:
$stateProvider
.state('threeCols.list', {
url: '/list',
controller: 'ListCtrl',
views: {
'left': {
templateUrl: 'modules/list/leftbar.html'
},
'middle': {
templateUrl: 'modules/list/content.html'
},
'right': {
templateUrl: 'modules/list/rightbar.html'
}
}
}).state('oneCol.create', {
url: '/create',
controller: 'CreateCtrl',
templateUrl: 'modules/create/content.html'
}).state('threeCols.view', {
url: '/view',
controller: 'ViewCtrl',
views: {
'left': {
templateUrl: 'modules/view/leftbar.html'
},
'middle': {
templateUrl: 'modules/view/content.html'
},
'right': {
templateUrl: 'modules/view/rightbar.html'
}
}
}).state('oneCol.edit', {
url: '/edit',
controller: 'EditCtrl',
templateUrl: 'modules/edit/content.html'
});
With this, now your URLs will look like what you expected: /pages/list
, /pages/create
, /pages/view
, /pages/edit
. You can also create and reuse templates as much as you like.
Upvotes: 5
Reputation: 33141
I usually define my routes based on view, not really based on the model. For example, given your simple explanation above, I would use the following:
$stateProvider
.state('pages.list', { url: '/list', controller: 'ListCtrl', templateUrl: 'threecol.html' })
.state('pages.create', { url: '/create', controller: 'CreateCtrl', templateUrl: 'create.html' })
.state('pages.item', { url: '/:itemId', controller: 'ItemCtrl', templateUrl: 'threecol.html' })
.state('pages.edit', { url: '/:itemId/edit', controller: 'EditCtrl', templateUrl: 'edit.html' });
Essentially then you would use threecol.html
to define your common three column layout. create.html
and edit.html
would be your full pages.
If you wanted a more abstract approach defined around the layout types, you could also create abstract parent states to define the layout itself. For example you could have:
threecol.list
threecol.item
fullpage.create
fullpage.edit
If you made threecol
and fullpage
abstract states, you could define the commonly used layout markup, and just insert the appropriate <div ui-view=""></div>
where desired. That way you could use different content template files for list
and item
for example.
Upvotes: 0