HansA
HansA

Reputation: 1375

AngularJS with ui-router: Changing value of a parent dependency of from child's controller

Environment: AngularJS application (using UI Router) that has nested states.

Short version: How do I change the value of a parent-state's dependency from inside a child's controller, such that siblings of the child state, who are also using the dependency will get the new value.

Long version: Take the following configuration:

shopApp.config(function($stateProvider) {
    $stateProvider.state('shop', {
        url : '/shop',
        templateUrl : 'shop.html',
        resolve : {
            user : function($q) {
                // callback that ends up returning null 
                // because the user is not logged in
            }
        },
        controller: function($scope, user) {
            // user is null at this point
            $scope.user = user;
        }
    }).state('shop.login', {
        url : '/login',
        templateUrl : 'login.html',
        controller : function($scope) {
            // onLoggedIn is called from some form-processing
            // logic once the user has successfully logged in
            $scope.onLoggedIn = function(userObject) {
                // I want the shop.userDetails state's controller
                // to get the new $scope.user value
                $scope.user = userObject;
                $state.go('^.userDetails');
            };
        }
    }).state('shop.userDetails', {
        url : '/userDetails',
        templateUrl : 'userDetails.html',
        controller : function($scope, user) {
            // Unfortunately user is null here
            $scope.user = user;
        }
    });
});

The shop.login state's controller has a function called onLoggedIn that's called once the user has logged in successfully. An object containing user-information is passed to this function. From this point on I want all resolutions of the user dependency to use this value (i.e userObj). Unfortunately, instead of using the value that I've just assigned to $scope.user, it seems that the value that gets passed to the controller of shop.userDetails is the one that was originally resolved when the shop state's controller was created.

Does anyone know how to do this? If I'm doing this all wrong please tell me.

Upvotes: 0

Views: 876

Answers (2)

Pul
Pul

Reputation: 1

There is a rather straightforward approach using scope objects with more semantics:

Looking at your problem, I think, the relevant information that you need to model here is not only the user, which can be null and changing which during login leads to your problem. Rather you want to share the login state of your application (or the application state, in more general), i.e. is a user logged in, since when , which user and so on. This is also good design, since you keep information together, which are likely to be changed or extended in common. So instead of a single property, which can be null and thus gets created in the wrong scope, use a complex property, which will be created at the topmost scope, underneath which it will be shared and visible, in your code example that woul be in the shopController:

// in ShopController
$scope.loginState = { logggedin: false, user: null }; 
// could add time information, grants and other stuff here as well

// in LoginController
$scope.loginState.loggedin = true;  
$scope.loginState.user = user;  
// crucial: this will only change the attributes at the object in ShopControllers Scope

In the user detail template you could then use {{loginState.user}} directly, the user will be fetched from the parent scope (shop), where it will have been set by the sibling (shop.login).

This works, because like in templates, if you access a scope property reading, the scope hierarchy is scanned bottom up, until the property is found. Of course, you have to be careful not to assign the #loginState itself in deeper scopes. But you may use and change the properties (or even create new ones) of the common loginState object in the top scope.

But if you like to use {{user}} in the template, e.g. to make it more readable and reusable, this can easily be done by the following:

// in UserDetailController
$scope.user = $scope.loginState.user;   

This works, because UserDetailController will be initialized anew after switching from shop.login to shop.userdetail in your example. In other cases, you might want to use a watch on $scope.loginState, create an event $userLoggedIn or similar approaches.

Upvotes: 0

idursun
idursun

Reputation: 6335

One way of doing what you want is to define a UserDetailsService that holds the logged in user information. You set the user information on login and resolve user value by using the service.

Upvotes: 1

Related Questions