Search code examples
angularjsangular-ui-routerangularjs-routing

AngularJS UI Router $state reload child state only


I am using UI router for tabs of a main menu as well as for links within one of the states (users). The users state has a list of users and when a user is clicked, I want to reload the child state only, but it is reloading both the child state and the parent state (users).

Here's my code:

    app.config(function ($stateProvider, $locationProvider, $urlRouterProvider) {
    $urlRouterProvider.otherwise("/");
    $locationProvider.html5Mode(true);
    $stateProvider
        .state('home', {
            abstract: true,
            template: '<div ui-view=""></div>',
            controller: 'HomeController as homeCtrl'
        })
        .state('users', {
            parent: 'home',
            url: '/users',
            templateUrl: '/User/Index',
            controller: 'UserController as userCtrl',
        })
        .state('users.createUser', {
            templateUrl: '/User/CreateUser',
            controller: 'CreateUserController as cuCtrl'
        })
        .state('users.editUser', {
            templateUrl: '/User/EditUser',
            controller: 'EditUserController as euCtrl',
            resolve: {
                userInfo: function (contextInfo, userData) {
                    return userData.get(contextInfo.getUserKey());
                }
            }
        })
        .state('users.addWebItemsForUser', {
            templateUrl: '/User/AddWebItemsForUser',
            controller: 'UserWebItemsController as uwiCtrl'
        })
        .state('users.addReportsForUser', {
            templateUrl: '/User/AddReportsForUser',
            controller: 'UserReportsController as urCtrl'
        })
        .state('users.userGroups', {
            templateUrl: '/User/UserGroups',
            controller: 'UserGroupController as userGroupCtrl'
        })
        //.state('users.logInWebApp', {
        //    template: '<h3>Logging into web app</h3>',
        //    resolve: {
        //        user: function() {
        //            return {
        //                //companyId: contextInfo.getCustomerKey()
        //                //userId:
        //                //password:
        //            }
        //        }
        //    },
        //    controller: function() {
        //        // need company code, username and password of user then need to open window with webapp url (will that work?) - do we still want to add this functionality?
        //    }
        //})
        .state('links', {
            url: '/links',
            templateUrl: '/Link/Index',
            controller: 'LinkController as linkCtrl'
        })
        .state('onlrpts', {
            url: '/onlineReports',
            templateUrl: '/OnlineReport/Index',
            controller: 'OnlineReportController as onlRptCtrl'
        })
        .state('reports', {
            url: '/customerReports',
            templateUrl: '/CustomerReport/Index',
            controller: 'CustomerReportController as custRptCtrl'
        });
})
.config(function($provide) {
    $provide.decorator('$state', function($delegate, $stateParams) {
        $delegate.forceReload = function() {
            return $delegate.go($delegate.$current.name, $stateParams, {
                reload: true,
                inherit: false,
                notify: true
            });
        };
        return $delegate;
    });
});

This is the function in my parent controller (UserController) that is called when a user is clicked:

            this.userTreeClick = function(e) {
        contextInfo.setUserKey(e.Key);
        contextInfo.setUserName(e.Value);
        userCtrl.userKey = e.Key;
        $state.forceReload();
    };

So say I was in the users.userGroups state, when I click on another user, I want only the users.userGroups state to be reloaded. Is this possible? I have looked for an answer via Google and here at StackOverflow, but I haven't found anything that is exactly like what I am trying to do. When I break to check the $state.$current.name - the name is correct - users.userGroups, but it reloads everything instead of just the child state. Any help is very much appreciated!


Solution

  • As documented in API Reference , we can use $state.reload:

    $state.reload(state)

    A method that force reloads the current state. All resolves are re-resolved, controllers reinstantiated, and events re-fired.

    Parameters:

    • state (optional)
      • A state name or a state object, which is the root of the resolves to be re-resolved.

    An example:

    //will reload 'contact.detail' and 'contact.detail.item' states
    $state.reload('contact.detail');
    

    Similar we can achieve with a $state.go() and its options parameter:

    $state.go(to, params, options)

    ...

    • options (optional)
      • location ...
      • inherit ...
      • relative ...
      • notify ...
      • reload (v0.2.5) - {boolean=false|string|object}, If true will force transition even if no state or params have changed. It will reload the resolves and views of the current state and parent states. If reload is a string (or state object), the state object is fetched (by name, or object reference); and \ the transition reloads the resolves and views for that matched state, and all its children states.

    Example from https://github.com/angular-ui/ui-router/issues/1612#issuecomment-77757224 by Chris T:

    { reload: true } // reloads all,
    { reload: 'foo.bar' } // reloads top.bar, and any children
    { reload: stateObj } // reloads state found in stateObj, and any children
    

    An example

    $state.go('contact', {...}, {reload: 'contact.detail'});