32

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' }
    });
Richard
  • 4,740
  • 4
  • 32
  • 39
Honus Wagner
  • 2,830
  • 13
  • 44
  • 61
  • 1
    Having a similar issue so I'm thinking through this, did you end up putting all of your previous code from the accounts controller and template into the new accounts list files? – aron.duby Jan 21 '15 at 05:27
  • @aron.duby Honestly, its been a while... I ended up not needing to list the accounts in this case, but from what I remember, no. If you see the answer from Matt Way, I used his modified code as my guidance. So the accounts controller contains all of the hierarchical logic in it; the model itself contains nothing. It just references the list and detail models. – Honus Wagner Feb 01 '15 at 14:32

2 Answers2

39

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' });
Matt Way
  • 32,319
  • 10
  • 79
  • 85
  • Perfect! A little adaptation of the code, but I took the path of maintaining a hierarchical structure (per your code sample) and now its just perfect. – Honus Wagner Feb 24 '14 at 14:42
  • @HonusWagner Could you share how you mixed your first code with Matt's answer? I am hitting the exact same issue at the moment :) – Thomas Feb 26 '14 at 16:24
  • @Thomas as requested. – Honus Wagner Feb 27 '14 at 09:58
  • @Matt and HonusWagner upvoted for brevity, clarity and simply sorting my mess out! – kJamesy May 29 '14 at 13:51
  • @Matt, could you (or anyone else) provide a quick example of creating another parent above to take care of the controller logic? I have a similar problem, where I have networks>computers>programs. when I click a program, I want its details to replace the content of the computer. If I change the state of program from network.computer.program to network.program, I lose the hierarchy, and I want to keep it. – cloudberry Mar 11 '15 at 11:45
  • I'm not exactly sure what you are asking here, and might be better as a separate question (possibly linking this one). It sounds like you want both options for programs, so why not just create two templates (one for network.computer.program, and one for network.program)? – Matt Way Mar 11 '15 at 13:17
  • This worked for me if I followed the additional advice from @HonusWagner when he updated his question. The 'abstract' template should contain the parent url, and the default child template should contain no url. This is the other way round from that indicated in this answer. – Holf Jun 13 '15 at 16:31
  • @Holf It shouldn't make any difference to the outcome. You should choose whichever url best matches the state name and context though IMO. – Matt Way Jun 14 '15 at 03:02
  • @MattWay, the coin has dropped now. Thanks! :-) – Holf Jun 14 '15 at 09:24
14

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

Jakub
  • 479
  • 5
  • 17