23

I'm using the excellent ui-router module in my application. As part of this, I'm using named views to manage the 'dynamic sub-navigation' I have in the app.

Consider the following:

$urlRouterProvider.otherwise('/person/list');

$stateProvider
    .state('person', {
        url: '/person',
        abstract: true,
    })
    .state('person.list', {
        url: '/list',
        views: {
            "main@": {
                templateUrl: "person.list.html",
                controller: 'PersonListController'
            }
        }
    })
    .state('person.details', {
        url: '/{id}',
        views: {
            'main@': {
                templateUrl: "person.details.html",
                controller: 'PersonController'
            },
            'nav@': {
                templateUrl: "person.nav.html",
                controller: 'PersonNavController'
            }
        }
    });

When users first visit the app, they are presented with a list of people. When they click on a person, they are taken to the details page. Pretty basic stuff. Here's the markup if it helps...

<div>
    <aside ui-view="nav"></aside>
    <div ui-view="main"></div>
</div>

However, the PersonNavController calls a REST service to get a list of people, so when viewing a person, the user is able to navigate sibling elements. Using the method above causes the template and controller to re-render, thus causing a delay after every click, despite the content never changing.

Is there a way to keep the 'nav@' view loaded, and only refresh the 'main@' view?

1 Answer 1

25

The way I am using ui-router in this scenarios is: move the views to the least common denominator.

Other words: In case that ui-view="nav" is shared among all the details and is the same for all of them (because it should be loaded only once) - it should be part of the list state (parent of the detail state)

the parent state defintion would be adjusted like this:

.state('person.list', {
    url: '/list',
    views: {
        "main@": {
            templateUrl: "person.list.html",
            controller: 'PersonListController'
        }
        // here we target the person.list.html
        // and its ui-view="nav"
        '[email protected]': {
            templateUrl: "person.nav.html",
            controller: 'PersonNavController'
        }
    }

So where is the trick? In the power of the angular ui-router. We can, during each state defintion, target the current view. Now, the nav view is part of the list state definition - i.e. it will not be reloaded during the detail switching (also check here for more explanation)

We just have to use the defined naming conventions, see:

Few cited lines from the mentioned documentation:

views: {
    ////////////////////////////////////
    // Relative Targeting             //
    // Targets parent state ui-view's //
    ////////////////////////////////////

    // Relatively targets the 'detail' view in this state's parent state, 'contacts'.
    // <div ui-view='detail'/> within contacts.html
    "detail" : { },            

    // Relatively targets the unnamed view in this state's parent state, 'contacts'.
    // <div ui-view/> within contacts.html
    "" : { }, 

    ///////////////////////////////////////////////////////
    // Absolute Targeting using '@'                      //
    // Targets any view within this state or an ancestor //
    ///////////////////////////////////////////////////////

    // Absolutely targets the 'info' view in this state, 'contacts.detail'.
    // <div ui-view='info'/> within contacts.detail.html
    "[email protected]" : { }

    // Absolutely targets the 'detail' view in the 'contacts' state.
    // <div ui-view='detail'/> within contacts.html
    "detail@contacts" : { }
Sign up to request clarification or add additional context in comments.

9 Comments

Why the down vote? I'd like to know what is wrong, to be able to improve the answer... Do not hesitate to leave a comment.
This is a great answer, it basically solves the problem which was a reason to create functionalities like 'sticky states' github.com/christopherthielen/ui-router-extras and similar
@RadimKöhler I found your answer an excellent one and just upvoted it, although I couldn't get what I could understand of it to work in my similar use case. Could you update it with the other state's (person.details) code as well, please?
Hi @RadimKöhler, thanks for your answer. I eventually managed to get it to work. Thanks again for your kindness! :)
@wiherek how does this solve the problem of sticky states? If I have sbleft, sbright and "", how can I make sure that sbright is never reloaded on route change using relative views? Let's say sbright contains a websocket connection for messages and I'd like to avoid reloading it because it would initiate a new websocket connection on each route change, can I do it using relative routes?
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.