Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Angular: Updating different controller through service or better method

I have a UserService

angular.module('mango.services', [])
    .factory('UserService', function() {
        var user = {
            id: null,
            name: 'anonymous.'
        };
        function getUser(){
            return user;
        }
        function setUser(val){
            user = val;
        }
        return {
            getUser: getUser,
            setUser: setUser,
        }
});

a NavbarController

.controller('NavbarController', ['$scope','$location','UserService', function($scope, $location, UserService){
    $scope.isActive = function (viewLocation) {
        return viewLocation === $location.path();
    };
    $scope.username = UserService.getUser().name;
}])

and a UserController where I have registerUser and loginUser functions.

.controller('UserController', ['$scope', '$http', 'UserService', function($scope, $http, UserService) {
   $scope.loginUser = function(){
        $http.post('/api/1.0/user/authenticate', $scope.user)
            .success(function(data,status,headers,config){
                if (data["valid"] === true) {
                    UserService.setUser(data.user);
                } else {
                    $scope.flashes = data.flashes;
                    $scope.user.password = "";
                }
             })
}

and the HTML

        <li ng-switch="username">
            <a ng-class="{ active: isActive('/user/login')}" href="#/user/login" ng-switch-when="anonymous."><i class="fa fa-sign-in"></i> Sign in</a>
            <a ng-class="{ active: isActive('/user/logout')}" href="#/user/logout" ng-switch-default><i class="fa fa-sign-out"></i> Sign out</a>
        </li>

As you can see I'm trying to set the user of UserService if data.valid is true.

The server is returning a valid json object.

But the username value in NavbarController remains "anonymous." .

I'm not very experienced in JS, but I read something about broadcast and watch. I believe this might be the right approach. But maybe there's a better one.

I believe why it's not working is because the factory returns a singleton. But then using a factory is pointless.

So essentially what I want is, if credentials valid set user.name user.id client-app-wide. Later it should go through an "check if client user is valid" service. My session cookie is encrypted. But that's out of scope of the question.

All I need right now is to set the app's or rather the NavbarController's user data from UserController. How do I do that so it also updates the DOM aka ng-switch getting a different value.


2 Answers

It is not working because you are not creating a binding of some kind: with $scope.username = UserService.getUser().name you get the user name at that instant of time (which is anonymous) and hold on to it forever. One way out of this is with a watch. In NavbarController, replace the previous code with:

$scope.$watch(
    function() {
        return UserService.getUser().name;
    },
    function(newval) {
        $scope.username = newval;
    }
);

This will incur your application with a function call in every digest cycle. This function call is not slow, so it wouldn't matter.

If you do not want this overhead, you can also do it with events. In NavbarController:

$scope.username = UserService.getUser().name;
$scope.on("loggedIn", function(event, newUserName) {
    $scope.username = newUserName;
});

And in UserController (add $rootScope to the dependencies):

if (data["valid"] === true) {
    UserService.setUser(data.user);
    $rootScope.$broadcast("loggedIn", data.user);
}
like image 114
Nikos Paraskevopoulos Avatar answered Aug 08 '26 20:08

Nikos Paraskevopoulos


You indeed do need a $watcher to sync username in the NavbarController and UserService instead of $scope.username = UserService.getUser().name; which only sets up the initial value of $scope.username when the Controller is initialised:

$scope.$watch(
    function () { return UserService.getUser().name; }, 
    function (newVal) {
        $scope.username = newVal;
    }
);

The factory indeed designed to be a singleton across the application and that is the place where you should save the state of the application (i.e. your models, like the User model). The controllers are ephemeral and are created anew each time a template is created.

However, if you have a long lived Controller (like your NavbarControll), then the onus of maintaining a sync between the services and the controller is on the programmer using $watch.

Broadcasting messages is useful in some cases when inter-controller communication (which does not necessarily involve the models) is needed.

like image 40
musically_ut Avatar answered Aug 08 '26 19:08

musically_ut



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!