1

I have the following service (pseudo code)

app.service('user', function($http) {
    function login(email, password, callback) {
        // login user
        // set isLoggedIn to true
        // assign the returned user object to userData
        // issue callback
    };

    return {
        isLoggedIn: false,
        userData: null,
        login: login
    };
});

Here is my login controller:

app.controller('LoginController', function($scope, $http, $location, user) {
    user.login($scope.email, $scope.password, function(isSuccess, data, status) {
        if (isSuccess) { $scope.onSuccessfulLogin(); }
        else { $scope.loginStatusMessage = data; }
    });

    $scope.onSuccessfulLogin = function() {
        $location.path('/someOtherPage');
    };
});

And here is the controller used in /someOtherPage

app.controller('SomeOtherPageController', function($scope, $http, $modal, user) {
    // lots of stuff in here
    // I put a breakpoint on the first line and user.isLoggedIn is false
    // even though it was definitely set to true when the user logged in.
)};

Once the callback has been issued when login in using the user service, if the login was successful the user is taken to a different page with a different controller, where the above user service is injected. The problem is that if the login is successful, although I can see the isLoggedIn and userData variables being assigned correctly, in the new controller they remain false and null respectively.

Have I missed something here ? I need the values to be the same whenever user is injected like a singleton.

Thanks

Sherlock
  • 5,125
  • 5
  • 40
  • 70
  • So I see you decided the service was the answer over the factory...can't say I agree but I guess it's up to u. – Sten Muchow Apr 17 '14 at 04:27
  • @StenMuchow - coming from a .net background this seems like a cleaner solution to me. I'm not sure if there are any benefits to your solution over the one I chose, but i'm purely going on that basis. – Sherlock Apr 17 '14 at 08:58
  • ok... check out the same thing in my edited answer. The thing i dont like about the other answer is the use of the that variable, but i am just picky... – Sten Muchow Apr 17 '14 at 09:02
  • and actually the code was incorrect up until i just edited it. Which i notice u cant actually see. but he forgot the function keyword that this.login needs to be set to... – Sten Muchow Apr 17 '14 at 09:05
  • @StenMuchow I noticed the missing function keyword also. I think both answers are viable - I will play around some more tonight when I get home from work. I'm .net developer by day, javascript by night. – Sherlock Apr 17 '14 at 09:49

4 Answers4

4

You need to use a service, not a factory. A service is returns an instance of your function. Which means once injected everyone is going to be using the same instance. In your case you are using a factory(you have updated it since I posted my answer), and every time its injected it returns the value returned from the function. The best SO post on this is here AngularJS: Service vs provider vs factory

I tend to use services when I want to drive this point home. I want people to know if they inject this, everyone is going to be sharing the same object. I often use it with Cache's and Users.

app.service('user', function($http) {
    var that = this;

    this.isLoggedIn = false;
    this.login = function(email, password, callback) {
       that.isLoggedIn = true;
       // login user
       // set isLoggedIn to true
       // assign the returned user object to userData
       // issue callback
    };
    return this;
})
Community
  • 1
  • 1
Nix
  • 52,320
  • 25
  • 137
  • 193
  • Hey Nix, I tried it with a service and it's still the same problem. Any thoughts ? Thanks. – Sherlock Apr 13 '14 at 20:36
  • 1
    No matter how you construct a service it will always be a singleton. So this is not a issue. – dfsq Apr 13 '14 at 20:39
  • Nix - added more code, maybe you could take a look ? – Sherlock Apr 14 '14 at 09:11
  • @Nix - I tried this and it's the same problem. I started a bounty .. any other thoughts ? – Sherlock Apr 16 '14 at 19:47
  • @Nix - sorry for spamming. I noticed that when I do this, and put a breakpoint into the login function, `this` is actually a window object, and none of the service variables have been defined. Does this point to the issue ? Thanks. – Sherlock Apr 16 '14 at 19:59
  • let us [continue this discussion in chat](http://chat.stackoverflow.com/rooms/50818/discussion-between-nix-and-sten-muchow) – Nix Apr 16 '14 at 20:21
  • @dfsq when he originally posted he had `app.factory(` . He has since updated it. – Nix Apr 17 '14 at 12:19
  • 1
    I know, factory also is going to create a singleton. As well as service. – dfsq Apr 17 '14 at 14:23
1

Actually all services in angular are singletons. Check this stackoverflow for more info. Anywho, onto your problem, and this directly from Angular docs:

Angular services are:

Lazily instantiated – Angular only instantiates a service when an application component depends on it.

Singletons – Each component dependent on a service gets a reference to the single instance generated by the service factory.

You should be using a factory as you just need to recreate the revealing module pattern, which Angular implements through the factory, the service is a constructor function.

Both of these are besides the point that your service is simply returning null and false for both values and no matter what you change them to internally in the service the values will always be null and false. You are returning an object so you need to wire the key value pair up correctly.

app.factory('user', function($http) {
    var service = {};

    service.userData = null;
    service.isLoggedIn = false;
    service.login = function(email, password, callback) {
        // login user
        // set isLoggedIn to true
        this.isLoggedIn = true;
        // assign the returned user object to userData
        this.userData = someValue;
        // issue callback
    } 

    return service;

});
Community
  • 1
  • 1
Sten Muchow
  • 6,373
  • 4
  • 33
  • 46
1

The problem here is that you are returning an object that is instantiated with null and false:

return {
        isLoggedIn: false,
        userData: null,
        login: login
    };

It should be returning another closure variable.

 return {
            isLoggedIn: myIsLoggedIn,
            userData: myUserData,
            login: login
        };

Where myUserData and myIsLoggedIn are set in the login function. Or another route is to return methods that return those values as @Sten mentioned.

mithun_daa
  • 4,046
  • 4
  • 33
  • 48
0

I think the problem is not in the service, but in the controller: probably you have a scope property set to isLoggeIn value, something like that:

$scope.isLoggedIn = user.isLoggedIn

but, without a watcher to user service value, your scope property will not change its value. Can you post controller code?

wilver
  • 1,992
  • 1
  • 17
  • 25
  • Hi wilver - I added in more code and details into the question. Maybe you could take a look if you get a chance ? Thanks. – Sherlock Apr 14 '14 at 09:07
  • Could you post more code on how you use user service in your controller? All in the code you posted seems correct... – wilver Apr 14 '14 at 09:18
  • I believe that your 'SomeOtherAppController' controller initialization run before login service complete and your scope is not watching for update. try to put a watcher on user service property, something like this: https://github.com/williamverdolini/discitur-web/blob/master/app/modules/user/UserNavBarCtrl.js#L68 – wilver Apr 14 '14 at 09:42
  • Yes I tried this - at no point is the user seen as logged in. Even when I go back to the login controller after logging in it is back to false .. no idea why that would happen. – Sherlock Apr 14 '14 at 10:04
  • so, the problem is in login method and how it updates the data, try to use var _user = {isLoggedIn:false,....} return _user; and in login method change _user properties – wilver Apr 14 '14 at 10:36