Angular UI-Router: child using parent's view
Asked Answered
B

2

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' }
    });
Beggary answered 24/2, 2014 at 12:42 Comment(2)
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?Interdepartmental
@Interdepartmental 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.Beggary
R
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' });
Reservoir answered 24/2, 2014 at 12:50 Comment(8)
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.Beggary
@HonusWagner Could you share how you mixed your first code with Matt's answer? I am hitting the exact same issue at the moment :)Fennelly
@Fennelly as requested.Beggary
@Matt and HonusWagner upvoted for brevity, clarity and simply sorting my mess out!Been
@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.Lewendal
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)?Reservoir
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.Bumgardner
@Bumgardner It shouldn't make any difference to the outcome. You should choose whichever url best matches the state name and context though IMO.Reservoir
A
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

Adopt answered 25/7, 2015 at 17:13 Comment(1)
using this, navigating back to the parent state reloads the parent's controller and viewRocha

© 2022 - 2024 — McMap. All rights reserved.