How do I change a scope variable with a service?

2019-07-07 02:23发布

I have an app, and inside the app is a signup for and login form.

With the signup form, when the user creates an account, they are automatically logged in.

With the login form, the user can also login (obviously).

I have two controllers: one for the signup form, and one for the login form. I would like them to be able to share a service, 'logIn', since with both forms the user ends up being logged in.

It's working so far. My only issue is that the login form asynchronously checks the user's login credentials against the database, and if the PHP script that handles this request comes back saying there wasn't a match, then angular updates the model of the error message, which is shown to the user. This works perfectly fine when the 'logIn' service isn't used as a service, but just copied into both controllers. Here's how it looks in 'SignupCtrl':

.controller('SignupCtrl',['$scope','$http',function($scope,$http) {
    $scope.error = false;

    $scope.logIn = function() {
        var data = // user's login credentials

        $http({
            // send credentials to server, ask if there's a match
        })
        .success(function(data) {
            if(data.match === false) {
                $scope.error = true;
            } else {
                // start session, redirect to new page, etc.
            }
        })
    }
}]);

Then, inside the template:

<div ng-show="error">Oops! That email/password doesn't exist.</div>

This works fine; but as I said, not when the 'logIn' function is used as a service. This is because I am unable to figure out how to update $scope.error inside the service.

I've tried using $rootScope (which didn't work):

.service('logIn',[/* injections */, '$rootScope', function(/* injections */, $rootScope) {
    return function(email, password) {
        // all the same code as before

        if(data.match === false) {
            $rootScope.error = true; // this does nothing
        }
    }
}]);

// in the controller (the service is injected in as 'logIn')

$scope.logIn = logIn;

Then, in the template:

<button ng-click="logIn(user.email, user.password)">Login</button>

The user can login just fine with the service. The problem is just updating the $scope.error variable with the service.

Maybe if I could somehow do this, it would work:

<button ng-click="logIn(user.email, user.password, $scope)">Login</button>

Then in the service:

return function(email, password, scope) {
   // etc.
   if(data.match === false) {
       scope.error = true;
   }
}

Any thoughts? Thank you.

Small edit

Just to clarify, from what I understand of services, they seem to take the place of how one would normally declare a global function, or just an overall function inside a module in order to avoid repeating one's self. Example:

(function() {
    function globalFunction() {
        //etc.
    }

    $('.thing').click(globalFunction);

    $('.otherThing').click(globalFunction);
}());

There could be a better example than that, but I think the idea is clear.

Is this concept similar to how services should be used inside angular? If not, is there a better way for me to go about doing what I'm trying to do, without using a service?

1条回答
Juvenile、少年°
2楼-- · 2019-07-07 02:39

You can pass in a $rootScope to a service, but it's generally not a good idea to pollute your $rootScope, however your problem in this particular case is that no digest was forced and thus your controllers $scope was unaware of the change. In the code you posted if you wrap your $rootScope.error inside a $rootScope.$apply() things will work fine. But a callback is much cleaner.

I suggest you pass a callback into the service method, and set your scope variable inside the callback:

login:function(email,password,callback){
    $http({
            // send credentials to server, ask if there's a match
        })
        .success(function(data) {
         callback(data.match);
        })
}

and in your controller:

$scope.logIn = function() {
    loginServcie.login($scope.email,$scope.password,function(dataIsMatched){
   //you now have the result of call, and dataIsMatched is true or false
    $scope.error = !dataIsMatched;
})
}
查看更多
登录 后发表回答